/* Tokens with data */ %token INT %token FLOAT /* A path */ %token PATH /* Search path, enclosed in <> */ %token SPATH /* Home path, starts with ~ */ %token HPATH %token URI %token STR_START %token STR_MID %token STR_END %token ISTR_START %token ISTR_MID %token ISTR_END %token ID /* Tokens that stand for themselves */ %token SELECT "." %token QMARK "?" %token CONCAT "++" %token NOT "!" %token MERGE "//" %token ASSIGN "=" %token LT "<" %token LTE "<=" %token GT ">" %token GTE ">=" %token EQ "==" %token NEQ "!=" %token AND "&&" %token OR "||" %token IMPL "->" %token AQUOTE_OPEN "${" %token AQUOTE_CLOSE "}$" %token LBRACE "{" %token RBRACE "}" %token LBRACK "[" %token RBRACK "]" %token PLUS "+" %token MINUS "-" %token TIMES "*" %token SLASH "/" %token LPAREN "(" %token RPAREN ")" %token COLON ":" %token SEMICOLON ";" %token COMMA "," %token ELLIPSIS "..." %token AS "@" /* Keywords */ %token WITH "with" %token REC "rec" %token LET "let" %token IN "in" %token INHERIT "inherit" %token IF "if" %token THEN "then" %token ELSE "else" %token ASSERT "assert" %token ORDEF "or" /* End of input */ %token EOF %{ open Types %} %start main %% main: | e = expr0 EOF { e } expr0: | "if"; e1 = expr0; "then"; e2 = expr0; "else"; e3 = expr0 { Cond (e1, e2, e3) } | "with"; e1 = expr0; ";"; e2 = expr0 { With (e1, e2) } | "assert"; e1 = expr0; ";"; e2 = expr0 { Assert (e1, e2) } | "let"; xs = delimited("{", list(binding), "}") { SetLet xs } | "let"; xs = list(binding); "in"; e = expr0 { Let (xs, e) } | l = lambda { Val l } | e = expr1 { e } /* Rules expr1-expr14 are almost direct translation of the operator precedence table: https://nixos.org/nix/manual/#sec-language-operators */ %inline binary_expr(Lhs, Op, Rhs): | lhs = Lhs; op = Op; rhs = Rhs { BinaryOp (op, lhs, rhs) } expr1: | e = binary_expr(expr2, "->" {Impl}, expr1) | e = expr2 { e } expr2: | e = binary_expr(expr2, "||" {Or}, expr3) | e = expr3 { e } expr3: | e = binary_expr(expr3, "&&" {And}, expr4) | e = expr4 { e } %inline expr4_ops: | "==" { Eq } | "!=" { Neq } expr4: | e = binary_expr(expr5, expr4_ops, expr5) | e = expr5 { e } %inline expr5_ops: | "<" { Lt } | ">" { Gt } | "<=" { Lte } | ">=" { Gte } expr5: | e = binary_expr(expr6, expr5_ops, expr6) | e = expr6 { e } expr6: | e = binary_expr(expr7, "//" {Merge}, expr6) | e = expr7 { e } expr7: | e = preceded("!", expr7) { UnaryOp (Not, e) } | e = expr8 { e } %inline expr8_ops: | "+" { Plus } | "-" { Minus } expr8: | e = binary_expr(expr8, expr8_ops, expr9) | e = expr9 { e } %inline expr9_ops: | "*" { Mult } | "/" { Div } expr9: | e = binary_expr(expr9, expr9_ops, expr10) | e = expr10 { e } expr10: | e = binary_expr(expr11, "++" {Concat}, expr10) | e = expr11 { e } expr11: | e = expr12 "?" p = attr_path { Test (e, p) } | e = expr12 { e } expr12: | e = preceded("-", expr13) { UnaryOp (Negate, e) } | e = expr13 { e } expr13: | f = expr13; arg = expr14 { Apply (f, arg) } | e = expr14 { e } %inline selectable: | s = set { Val s } | id = ID { Id id } | e = delimited("(", expr0, ")") { e } expr14: | e = selectable; "."; p = attr_path; o = option(preceded("or", expr14)) { Select (e, p, o) } | e = atomic_expr; "or" { Apply (e, Id "or") } | e = atomic_expr { e } atomic_expr: | id = ID { Id id } | v = value { Val v } | e = delimited("(", expr0, ")") { e } attr_path: | p = separated_nonempty_list(".", attr_path_component) { p } attr_path_component: | "or" { Id "or" } | id = ID { Id id } | e = delimited("${", expr0, "}$") { Aquote e } | s = str { Val s } value: | s = str { s } | s = istr { s } | i = INT {Int i} | f = FLOAT { Float f } | p = PATH { Path p } | sp = SPATH { SPath sp } | hp = HPATH { HPath hp } | uri = URI { Uri uri } | l = nixlist { l } | s = set { s } %inline str_mid(X): | xs = list(pair(delimited("${", expr0, "}$"), X)) { xs } /* Double-quoted string */ str: | start = STR_START; mids = str_mid(STR_MID); STR_END { Str (start, mids) } /* Indented string */ istr: | start = ISTR_START; mids = str_mid(ISTR_MID); i = ISTR_END { IStr (i, start, mids) } /* Lists and sets */ nixlist: | xs = delimited("[", list(expr14), "]") { List xs } empty_set: | "{"; "}" {} set: | empty_set { AttSet (Nonrec, []) } | xs = delimited("{", nonempty_list(binding), "}") { AttSet (Nonrec, xs) } | xs = preceded("rec", delimited("{", list(binding), "}")) { AttSet (Rec, xs) } binding: | kv = terminated(separated_pair(attr_path, "=", expr0), ";") { let (k, v) = kv in AttrPath (k, v) } | xs = delimited("inherit", pair(option(delimited("(", expr0, ")")), list(attr_path_component)), ";") { let (prefix, ids) = xs in Inherit (prefix, ids) } lambda: | id = ID; "@"; p = param_set; ":"; e = expr0 { Lambda (ParamSet (Some id, p), e) } | p = param_set; "@"; id = ID; ":"; e = expr0 { Lambda (ParamSet (Some id, p), e) } | p = param_set; ":"; e = expr0 { Lambda (ParamSet (None, p), e) } | id = ID; ":"; e = expr0 { Lambda (Alias id, e) } %inline param_set: | empty_set { ([], Exact) } | "{"; "..."; "}" { ([], Loose) } | ps = delimited("{", pair(pair(params, ","?), boption("...")), "}") { let ((ps, _), ellipsis) = ps in (ps, if ellipsis then Loose else Exact) } params: | p = param { [p] } | ps = params; ","; p = param { ps @ [p] } %inline param: p = pair(ID, option(preceded("?", expr0))) { p }