graphix-compiler 0.8.0

A dataflow language for UIs and network programming, compiler
Documentation
use super::{
    csep, fname, ident, not_prefix, sep_by1_tok, sep_by_tok, spaces, spaces1, spfname,
    spstring, sptoken, typname,
};
use crate::{
    expr::{Expr, ExprKind, ModPath, TypeDefExpr},
    typ::{AbstractId, FnArgKind, FnArgType, FnType, TVar, Type, TypeRef},
};
use arcstr::ArcStr;
use combine::{
    attempt, between, choice, look_ahead, not_followed_by, optional,
    parser::char::{alpha_num, string},
    position, sep_by1,
    stream::{position::SourcePosition, Range},
    token, unexpected_any, value, ParseError, Parser, RangeStream,
};
use fxhash::FxHashSet;
use netidx::{publisher::Typ, utils::Either};
use parking_lot::RwLock;
use poolshark::local::LPooled;
use triomphe::Arc;

pub(super) fn typath<I>() -> impl Parser<I, Output = ModPath>
where
    I: RangeStream<Token = char>,
    I::Error: ParseError<I::Token, I::Range, I::Position>,
    I::Range: Range,
{
    sep_by1(spaces().with(choice((fname(), typname()))), string("::")).then(
        |mut parts: LPooled<Vec<ArcStr>>| {
            if parts.len() == 0 {
                unexpected_any("empty type path").left()
            } else {
                match parts.last().unwrap().chars().next() {
                    None => unexpected_any("empty name").left(),
                    Some(c) if c.is_lowercase() => {
                        unexpected_any("type names must be capitalized").left()
                    }
                    Some(_) => value(ModPath::from(parts.drain(..))).right(),
                }
            }
        },
    )
}

fn typeprim<I>() -> impl Parser<I, Output = Typ>
where
    I: RangeStream<Token = char>,
    I::Error: ParseError<I::Token, I::Range, I::Position>,
    I::Range: Range,
{
    choice((
        string("string").map(|_| Typ::String),
        string("error").map(|_| Typ::Error),
        string("array").map(|_| Typ::Array),
        string("null").map(|_| Typ::Null),
        attempt(string("i8")).map(|_| Typ::I8),
        attempt(string("i16")).map(|_| Typ::I16),
        attempt(string("i32")).map(|_| Typ::I32),
        string("i64").map(|_| Typ::I64),
        attempt(string("u8")).map(|_| Typ::U8),
        attempt(string("u16")).map(|_| Typ::U16),
        attempt(string("u32")).map(|_| Typ::U32),
        string("u64").map(|_| Typ::U64),
        attempt(string("v32")).map(|_| Typ::V32),
        string("v64").map(|_| Typ::V64),
        attempt(string("z32")).map(|_| Typ::Z32),
        string("z64").map(|_| Typ::Z64),
        attempt(string("f32")).map(|_| Typ::F32),
        string("f64").map(|_| Typ::F64),
        attempt(string("decimal")).map(|_| Typ::Decimal),
        attempt(string("datetime")).map(|_| Typ::DateTime),
        string("duration").map(|_| Typ::Duration),
        attempt(string("bytes")).map(|_| Typ::Bytes),
        string("bool").map(|_| Typ::Bool),
    ))
    .skip(not_prefix())
}

fn fnconstraints<I>() -> impl Parser<I, Output = Arc<RwLock<LPooled<Vec<(TVar, Type)>>>>>
where
    I: RangeStream<Token = char, Position = SourcePosition>,
    I::Error: ParseError<I::Token, I::Range, I::Position>,
    I::Range: Range,
{
    spaces()
        .with(optional(between(
            token('<'),
            sptoken('>'),
            sep_by1_tok(
                (spaces().with(tvar()).skip(sptoken(':')), typ()),
                csep(),
                token('>'),
            ),
        )))
        .map(|cs: Option<LPooled<Vec<(TVar, Type)>>>| match cs {
            Some(cs) => Arc::new(RwLock::new(cs)),
            None => Arc::new(RwLock::new(LPooled::take())),
        })
}

fn fnlabeled<I>() -> impl Parser<I, Output = FnArgType>
where
    I: RangeStream<Token = char, Position = SourcePosition>,
    I::Error: ParseError<I::Token, I::Range, I::Position>,
    I::Range: Range,
{
    choice((string("?#").map(|_| true), string("#").map(|_| false))).then(|optional| {
        (fname().skip(sptoken(':')), typ()).map(move |(name, typ)| FnArgType {
            kind: FnArgKind::Labeled { name: name.into(), has_default: optional },
            typ,
        })
    })
}

fn fnpositional<I>() -> impl Parser<I, Output = FnArgType>
where
    I: RangeStream<Token = char, Position = SourcePosition>,
    I::Error: ParseError<I::Token, I::Range, I::Position>,
    I::Range: Range,
{
    (fname().skip(sptoken(':')), typ()).map(|(name, typ)| FnArgType {
        kind: FnArgKind::Positional { name: Some(name.into()) },
        typ,
    })
}

fn fnargs<I>() -> impl Parser<I, Output = LPooled<Vec<Either<FnArgType, Type>>>>
where
    I: RangeStream<Token = char, Position = SourcePosition>,
    I::Error: ParseError<I::Token, I::Range, I::Position>,
    I::Range: Range,
{
    spaces().with(between(
        token('('),
        sptoken(')'),
        sep_by_tok(
            spaces().then(|_| {
                choice((
                    string("@args:").with(typ()).map(|e| Either::Right(e)),
                    fnlabeled().map(Either::Left),
                    fnpositional().map(Either::Left),
                ))
            }),
            csep(),
            token(')'),
        ),
    ))
}

pub(super) fn fntype<I>() -> impl Parser<I, Output = FnType>
where
    I: RangeStream<Token = char, Position = SourcePosition>,
    I::Error: ParseError<I::Token, I::Range, I::Position>,
    I::Range: Range,
{
    attempt(string("fn").skip(not_followed_by(choice((token('_'), alpha_num())))))
        .with((
            fnconstraints(),
            fnargs(),
            spstring("->").with(typ()),
            optional(
                attempt(spaces1().with(string("throws"))).with(spaces1()).with(typ()),
            ),
        ))
        .then(|(constraints, mut args, rtype, throws)| {
            let vargs = match args.pop() {
                None => None,
                Some(Either::Right(t)) => Some(t),
                Some(Either::Left(t)) => {
                    args.push(Either::Left(t));
                    None
                }
            };
            if !args.iter().all(|a| a.is_left()) {
                return unexpected_any("vargs must appear once at the end of the args")
                    .left();
            }
            let args = Arc::from_iter(args.drain(..).map(|t| match t {
                Either::Left(t) => t,
                Either::Right(_) => unreachable!(),
            }));
            let mut anon = false;
            for a in args.iter() {
                if anon && a.is_labeled() {
                    return unexpected_any(
                        "anonymous args must appear after labeled args",
                    )
                    .left();
                }
                anon |= a.is_positional();
            }
            let explicit_throws = throws.is_some();
            let throws = throws.unwrap_or(Type::Bottom);
            value(FnType {
                args,
                vargs,
                rtype,
                constraints,
                throws,
                explicit_throws,
                ..Default::default()
            })
            .right()
        })
}

pub(super) fn tvar<I>() -> impl Parser<I, Output = TVar>
where
    I: RangeStream<Token = char, Position = SourcePosition>,
    I::Error: ParseError<I::Token, I::Range, I::Position>,
    I::Range: Range,
{
    token('\'').with(fname()).map(TVar::empty_named)
}

fn varianttyp<I>() -> impl Parser<I, Output = Type>
where
    I: RangeStream<Token = char, Position = SourcePosition>,
    I::Error: ParseError<I::Token, I::Range, I::Position>,
    I::Range: Range,
{
    (
        token('`').with(ident(true)),
        optional(attempt(between(
            token('('),
            sptoken(')'),
            sep_by1_tok(typ(), csep(), token(')')),
        ))),
    )
        .map(|(tag, typs): (ArcStr, Option<LPooled<Vec<Type>>>)| {
            let mut t = match typs {
                None => LPooled::take(),
                Some(v) => v,
            };
            Type::Variant(tag.clone(), Arc::from_iter(t.drain(..)))
        })
}

fn structtyp<I>() -> impl Parser<I, Output = Type>
where
    I: RangeStream<Token = char, Position = SourcePosition>,
    I::Error: ParseError<I::Token, I::Range, I::Position>,
    I::Range: Range,
{
    between(
        token('{'),
        sptoken('}'),
        sep_by1_tok((spfname().skip(sptoken(':')), typ()), csep(), token('}')),
    )
    .then(|mut exps: LPooled<Vec<(ArcStr, Type)>>| {
        let s = exps.iter().map(|(n, _)| n).collect::<LPooled<FxHashSet<_>>>();
        if s.len() < exps.len() {
            return unexpected_any("struct field names must be unique").left();
        }
        drop(s);
        exps.sort_by_key(|(n, _)| n.clone());
        value(Type::Struct(Arc::from_iter(exps.drain(..)))).right()
    })
}

fn tupletyp<I>() -> impl Parser<I, Output = Type>
where
    I: RangeStream<Token = char, Position = SourcePosition>,
    I::Error: ParseError<I::Token, I::Range, I::Position>,
    I::Range: Range,
{
    between(token('('), sptoken(')'), sep_by1_tok(typ(), csep(), token(')'))).map(
        |mut exps: LPooled<Vec<Type>>| {
            if exps.len() == 1 {
                exps.pop().unwrap()
            } else {
                Type::Tuple(Arc::from_iter(exps.drain(..)))
            }
        },
    )
}

pub(super) fn typref<I>() -> impl Parser<I, Output = Type>
where
    I: RangeStream<Token = char, Position = SourcePosition>,
    I::Error: ParseError<I::Token, I::Range, I::Position>,
    I::Range: Range,
{
    (
        position(),
        typath(),
        look_ahead(optional(attempt(sptoken('<')))).then(|o| match o {
            None => value(None).left(),
            Some(_) => between(
                sptoken('<'),
                sptoken('>'),
                sep_by1_tok(typ(), csep(), token('>')),
            )
            .map(Some)
            .right(),
        }),
    )
        .map(
            |(pos, n, params): (SourcePosition, ModPath, Option<LPooled<Vec<Type>>>)| {
                let params = params
                    .map(|mut a| Arc::from_iter(a.drain(..)))
                    .unwrap_or_else(|| Arc::from_iter([]));
                Type::Ref(TypeRef {
                    scope: ModPath::root(),
                    name: n,
                    params,
                    pos: Some(pos),
                    ori: Some(crate::expr::get_origin()),
                })
            },
        )
}

parser! {
    pub(super) fn typ[I]()(I) -> Type
    where [I: RangeStream<Token = char, Position = SourcePosition>, I::Range: Range]
    {
        spaces().with(choice((
            token('&').with(typ()).map(|t| Type::ByRef(Arc::new(t))),
            token('_').map(|_| Type::Bottom),
            between(token('['), sptoken(']'), sep_by_tok(typ(), csep(), token(']')))
                .map(|mut ts: LPooled<Vec<Type>>| Type::flatten_set(ts.drain(..))),
            tupletyp(),
            structtyp(),
            varianttyp(),
            fntype().map(|f| Type::Fn(Arc::new(f))),
            attempt(string("Array").skip(not_prefix())).with(between(sptoken('<'), sptoken('>'), typ()))
                .map(|t| Type::Array(Arc::new(t))),
            attempt(string("Any").skip(not_prefix())).map(|_| Type::Any),
            attempt(string("Map").skip(not_prefix())).with(between(
                sptoken('<'), sptoken('>'),
                (typ().skip(sptoken(',')), typ())
            )).map(|(k, v)| Type::Map { key: Arc::new(k), value: Arc::new(v) }),
            attempt(string("Error").skip(not_prefix())).with(between(sptoken('<'), sptoken('>'), typ()))
                .map(|t| Type::Error(Arc::new(t))),
            attempt(typeprim()).map(|typ| Type::Primitive(typ.into())),
            tvar().map(|tv| Type::TVar(tv)),
            typref(),
        )))
    }
}

pub(super) fn typedef<I>() -> impl Parser<I, Output = Expr>
where
    I: RangeStream<Token = char, Position = SourcePosition>,
    I::Error: ParseError<I::Token, I::Range, I::Position>,
    I::Range: Range,
{
    (
        position(),
        attempt(string("type").skip(spaces1())).with(typname()),
        spaces().with(optional(between(
            token('<'),
            sptoken('>'),
            sep_by1_tok(
                (
                    spaces().with(tvar()),
                    spaces().then(|_| optional(token(':').with(typ()))),
                ),
                csep(),
                token('>'),
            ),
        ))),
        spaces().with(optional(token('=').with(typ()))),
    )
        .map(|(pos, name, params, typ)| {
            let params = params
                .map(|mut ps: LPooled<Vec<(TVar, Option<Type>)>>| {
                    Arc::from_iter(ps.drain(..))
                })
                .unwrap_or_else(|| Arc::<[(TVar, Option<Type>)]>::from(Vec::new()));
            let typ = match typ {
                Some(typ) => typ,
                None => {
                    let params = Arc::from_iter(
                        params.iter().map(|(tv, _)| Type::TVar(tv.clone())),
                    );
                    Type::Abstract { id: AbstractId::new(), params }
                }
            };
            ExprKind::TypeDef(TypeDefExpr { name, params, typ }).to_expr(pos)
        })
}