From 344a75b7a175c34fd90564b4d05ae16c5b12a4ab Mon Sep 17 00:00:00 2001 From: Sven Slootweg Date: Thu, 12 Aug 2021 19:12:13 +0200 Subject: [PATCH] WIP --- src/expression.pegjs | 84 +++++++++++++++++++++++++++++++------------ test/fail-list-if.nix | 1 + test/fail-plus-if.nix | 1 + test/let-inherit.nix | 1 + test/with-if.nix | 1 + 5 files changed, 66 insertions(+), 22 deletions(-) create mode 100644 test/fail-list-if.nix create mode 100644 test/fail-plus-if.nix create mode 100644 test/let-inherit.nix create mode 100644 test/with-if.nix diff --git a/src/expression.pegjs b/src/expression.pegjs index 9a4f32c..ca2e0dd 100644 --- a/src/expression.pegjs +++ b/src/expression.pegjs @@ -1,4 +1,6 @@ { + /* FIXME: `or` operator */ + let operators = [{ associativity: "left", operators: ["call"] @@ -101,7 +103,7 @@ escapableCharacter / "t" / "r" -commentCharacter +inlineCommentCharacter = [^\n\r] // Literals @@ -129,14 +131,20 @@ homePath storePath = "<" path:([a-zA-Z0-9\.\_\-\+]+ ("/" [a-zA-Z0-9\.\_\-\+]+)*) ">" { return {type: "path", pathType: "store", storePath: path}; } +uri + = [a-zA-Z][a-zA-Z0-9+\-.]* ":" [a-zA-Z0-9%/?:@&=+$,\-_.!~*']+ + // Utilities _ - = (whitespace / comment)* {} + = (whitespace / inlineComment)* {} __ = whitespace+ {} -reservedWord // FIXME: Missing reserved words? +reservedWord + = name:reservedWordName (& [^a-zA-Z0-9._-]) { return name; } // FIXME: Unify identifier character set definition + +reservedWordName // FIXME: Missing reserved words? = "if" / "then" / "else" @@ -171,11 +179,13 @@ nonOperatorExpression / withStatement / conditional / path - / identifier + / uri + / attributePathAlternate -comment - = "#" chars:commentCharacter* _ { return {type: "comment", text: chars.join("")} } +inlineComment + = "#" chars:inlineCommentCharacter* _ { return {type: "inlineComment", text: chars.join("")} } +/* FIXME: Where else to allow blockComments? Needs to be allowed everywhere, not just in expressions. */ blockComment = "/*" segments:(! "*/" . [^*]*)* "*/" { return {type: "blockComment", text: joinBlockText(segments)} } @@ -187,7 +197,7 @@ functionCallOperatorExpression hasAttributeOperatorExpression = left:nonOperatorExpression _ "?" - _ right:identifier { return {type: "operatorExpression", operator: "?", left: left, right: right} } // FIXME: attribute path + _ right:identifier { return {type: "operatorExpression", operator: "?", left: left, right: right} } /* FIXME: Is an attribute path allowed here? */ binaryOperatorExpression = left:nonOperatorExpression @@ -197,22 +207,49 @@ binaryOperatorExpression divisionOperatorExpression = left:nonOperatorExpression _ "/" - _ right:expression { return {type: "operatorExpression", operator: "/", left: left, right: right} } + _ right:expression { + return {type: "operatorExpression", operator: "/", left: left, right: right}; + } numericallyNegatedOperatorExpression = "-" - _ right:expression { return {type: "operatorExpression", operator: "numericalNegate", right: right} } + _ right:expression { + return {type: "operatorExpression", operator: "numericalNegate", right: right}; + } booleanNegatedOperatorExpression = "!" - _ right:expression { return {type: "operatorExpression", operator: "booleanNegate", right: right} } + _ right:expression { + return {type: "operatorExpression", operator: "booleanNegate", right: right}; + } + +attributePathAlternate + = attributePath:attributePath alternate:(_ "or" _ attributePathAlternate)? { + if (alternate != null) { + return {type: "attributePathAlternate", path: attributePath, alternate: alternate}; + } else { + return attributePath; + } + } + +attributePath + = firstIdentifier:attributeItem nextIdentifiers:("." attributeItem)* { + return {type: "attributePath", items: [firstIdentifier].concat(nextIdentifiers.map(item => item[1]))}; + } + +attributeItem + = "\"" identifier:identifier "\"" { return identifier; } /* FIXME: What characters should be allowed here? */ + / identifier:identifier { return identifier; } identifier - = identifier:(! (reservedWord [^a-z0-9._-]) identifierValue) { return {type: "identifier", identifier: identifier[1]} } + = identifier:(! reservedWord identifierValue) { + return {type: "identifier", identifier: identifier[1]}; + } +/* FIXME: Quoted attributes? */ identifierValue = literal:stringLiteral { return literal.value; } - / chars:[a-z_]i [a-z0-9._-]i+ { return text(); } + / chars:[a-z_]i [a-z0-9_-]i+ { return text(); } group = "(" @@ -233,17 +270,18 @@ functionDefinitionArgument = identifier / setPattern +/* FIXME: Is `"foo" = "bar"` valid in an assignment list? */ assignment - = identifier:identifier + = attributePath:attributePath _ "=" _ expression:reorderedExpression - _ ";" { return {type: "assignment", identifier: identifier, expression: expression} } + _ ";" { return {type: "assignment", attributePath: attributePath, expression: expression} } assignmentList - = _ items:((assignment / inheritStatement) _)* { return items.map(item => item[0]); } + = _ items:((assignment / inheritStatement / blockComment) _)* { return items.map(item => item[0]); } -bindingList - = items:(assignment _)* { return {type: "bindings", assignments: items.map(item => item[0])} } +bindingList // FIXME: Allow inherit - point at assignmentList instead? + = items:((assignment / blockComment) _)* { return {type: "bindings", assignments: items.map(item => item[0])} } set = "{" @@ -279,18 +317,20 @@ elseClause = "else" _ alternative:expression { return alternative; } +/* FIXME: What to return here? */ withStatement = "with" - _ identifier:identifier + _ withExpression:expression _ ";" _ expression -inheritStatement // FIXME: Distinguish between identifiers and attribute paths? +inheritStatement /* FIXME: Distinguish between identifiers and attribute paths? */ = "inherit" - _ namespace:("(" identifier ")")? - _ identifiers:(identifier _)+ - _ ";" { return {type: "inheritStatement", identifiers: identifiers.map(item => item[0]), namespace: maybe(namespace, 1)} } + _ namespace:("(" reorderedExpression ")")? + _ attributePaths:(attributePathAlternate _)+ /* FIXME: Is attributePathAlternate allowed here, or just attributePath? */ + _ ";" { return {type: "inheritStatement", attributePaths: attributePaths.map(item => item[0]), namespace: maybe(namespace, 1)} } +/* FIXME: Are attribute paths allowed in set patterns? */ setPattern = "{" _ args:setPatternVariableList diff --git a/test/fail-list-if.nix b/test/fail-list-if.nix new file mode 100644 index 0000000..c66bc21 --- /dev/null +++ b/test/fail-list-if.nix @@ -0,0 +1 @@ +[if a then b else c] diff --git a/test/fail-plus-if.nix b/test/fail-plus-if.nix new file mode 100644 index 0000000..6ea250d --- /dev/null +++ b/test/fail-plus-if.nix @@ -0,0 +1 @@ +1 + if true then 1 else 2 diff --git a/test/let-inherit.nix b/test/let-inherit.nix new file mode 100644 index 0000000..262914b --- /dev/null +++ b/test/let-inherit.nix @@ -0,0 +1 @@ +let inherit bar; in {} diff --git a/test/with-if.nix b/test/with-if.nix new file mode 100644 index 0000000..66601e4 --- /dev/null +++ b/test/with-if.nix @@ -0,0 +1 @@ +with if false then {} else {x = 2;}; x