Skip to content

Commit

Permalink
Add Pkl language (#6730)
Browse files Browse the repository at this point in the history
* add Pkl language

* remove .pcf and PklProject as valid Pkl files

* add heuristics test for Pkl
  • Loading branch information
stackoverflow authored Jun 7, 2024
1 parent 6c26c74 commit ebe6ef7
Show file tree
Hide file tree
Showing 11 changed files with 495 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,9 @@
[submodule "vendor/grammars/pike-textmate"]
path = vendor/grammars/pike-textmate
url = https://github.com/hww3/pike-textmate
[submodule "vendor/grammars/pkl.tmbundle"]
path = vendor/grammars/pkl.tmbundle
url = https://github.com/apple/pkl.tmbundle.git
[submodule "vendor/grammars/polar-grammar"]
path = vendor/grammars/polar-grammar
url = https://github.com/osohq/polar-grammar.git
Expand Down
2 changes: 2 additions & 0 deletions grammars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,8 @@ vendor/grammars/pig-latin:
- source.pig_latin
vendor/grammars/pike-textmate:
- source.pike
vendor/grammars/pkl.tmbundle:
- source.pkl
vendor/grammars/polar-grammar:
- source.polar
vendor/grammars/portugol-grammar:
Expand Down
7 changes: 7 additions & 0 deletions lib/linguist/heuristics.yml
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,13 @@ disambiguations:
pattern: '<\?hh'
- language: PHP
pattern: '<\?[^h]'
- extensions: ['.pkl']
rules:
- language: Pkl
pattern:
- '^\s*(module|import|amends|extends|local|const|fixed|abstract|open|class|typealias|@\w+)\b'
- '^\s*[a-zA-Z0-9_$]+\s*(=|{|:)|^\s*`[^`]+`\s*(=|{|:)|for\s*\(|when\s*\('
- language: Pickle
- extensions: ['.pl']
rules:
- language: Prolog
Expand Down
10 changes: 10 additions & 0 deletions lib/linguist/languages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5320,6 +5320,16 @@ Pip Requirements:
ace_mode: text
tm_scope: source.pip-requirements
language_id: 684385621
Pkl:
type: programming
color: "#6b9543"
extensions:
- ".pkl"
interpreters:
- pkl
tm_scope: source.pkl
ace_mode: text
language_id: 288822799
PlantUML:
type: data
color: "#fbbd16"
Expand Down
130 changes: 130 additions & 0 deletions samples/Pkl/Parser.pkl
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
/// Utility methods for parsing values into [JsonSchema].
@ModuleInfo { minPklVersion = "0.25.0" }
module org.json_schema.Parser

import "pkl:json"
import "pkl:reflect"

import "JsonSchema.pkl"

local jsonParser = new json.Parser { useMapping = true }

local knownSchemaKeys: Set<String> = reflect.Module(JsonSchema).moduleClass.properties.keys

local function toJsonSchema(
raw: Mapping|Boolean,
baseSchema: JsonSchema?,
path: List<String|Int>
): JsonSchema.Schema =
if (raw is Boolean)
raw
else
new {
$$baseSchema = baseSchema ?? this
$id = raw.getOrNull("$id")
$ref = raw.getOrNull("$ref")
$schema = raw.getOrNull("$schema")
$comment = raw.getOrNull("$comment")
$defs = (raw.getOrNull("$defs") as Mapping?)
?.toMap()
?.map((key, value) -> Pair(key, toJsonSchema(value, $$baseSchema, path.add("definitions").add(key))))
?.toMapping()
definitions = (raw.getOrNull("definitions") as Mapping?)
?.toMap()
?.map((key, value) -> Pair(key, toJsonSchema(value, $$baseSchema, path.add("$defs").add(key))))
?.toMapping()
type = raw.getOrNull("type")
format = raw.getOrNull("format")
pattern = raw.getOrNull("pattern")
maxLength = raw.getOrNull("maxLength")
minLength = raw.getOrNull("minLength")
title = raw.getOrNull("title")
description = raw.getOrNull("description")
multipleOf = raw.getOrNull("multipleOf")
minimum = raw.getOrNull("minimum")
maximum = raw.getOrNull("maximum")
exclusiveMinimum = raw.getOrNull("exclusiveMinimum")
exclusiveMaximum = raw.getOrNull("exclusiveMaximum")
examples = raw.getOrNull("examples")
allOf = (raw.getOrNull("allOf") as Listing?)
?.toList()
?.mapIndexed((idx, elem) -> toJsonSchema(elem, $$baseSchema, path.add("allOf").add(idx)))
?.toListing()
anyOf = (raw.getOrNull("anyOf") as Listing?)
?.toList()
?.mapIndexed((idx, elem) -> toJsonSchema(elem, $$baseSchema, path.add("anyOf").add(idx)))
?.toListing()
oneOf = (raw.getOrNull("oneOf") as Listing?)
?.toList()
?.mapIndexed((idx, elem) -> toJsonSchema(elem, $$baseSchema, path.add("oneOf").add(idx)))
?.toListing()
not = if (raw.containsKey("not")) toJsonSchema(raw["not"], $$baseSchema, path.add("not")) else null
properties = (raw.getOrNull("properties") as Mapping?)
?.toMap()
?.map((key, value) -> Pair(key, toJsonSchema(value, $$baseSchema, path.add("properties").add(key))))
?.toMapping()
items =
let (rawItems = raw.getOrNull("items"))
if (rawItems == null) null
else if (rawItems is Listing)
rawItems
.toList()
.mapIndexed((idx, elem) -> toJsonSchema(elem, $$baseSchema, path.add("items").add(idx)))
.toListing()
else toJsonSchema(rawItems, $$baseSchema, path.add("items"))
additionalItems =
let (rawAdditionalItems = raw.getOrNull("additionalItems"))
if (rawAdditionalItems == null) null
else toJsonSchema(rawAdditionalItems, $$baseSchema, path.add("additionalItems"))
contains =
let (rawContains = raw.getOrNull("contains"))
if (rawContains == null) null
else toJsonSchema(rawContains, $$baseSchema, path.add("contains"))
minItems = raw.getOrNull("minItems")
maxItems = raw.getOrNull("maxItems")
uniqueItems = raw.getOrNull("uniqueItems")
default = raw.getOrNull("default")
deprecated = raw.getOrNull("deprecated")
readOnly = raw.getOrNull("readOnly")
writeOnly = raw.getOrNull("writeOnly")
enum = raw.getOrNull("enum")
required = raw.getOrNull("required")
`const` = raw.getOrNull("const")
propertyNames = let (_raw = raw.getOrNull("propertyNames"))
if (_raw == null) null
else toJsonSchema(_raw, $$baseSchema, path.add("propertyNames"))
maxProperties = raw.getOrNull("maxProperties")
minProperties = raw.getOrNull("minProperties")
additionalProperties =
let (rawAdditionalProperties = raw.getOrNull("additionalProperties"))
if (rawAdditionalProperties == null) null
else toJsonSchema(rawAdditionalProperties, $$baseSchema, path.add("additionalProperties"))
patternProperties = (raw.getOrNull("patternProperties") as Mapping?)
?.toMap()
?.map((key, value) -> Pair(key, toJsonSchema(value, $$baseSchema, path.add("patternProperties").add(key))))
?.toMapping()
_inline_ = raw.toMap()
.filter((key, value) -> !knownSchemaKeys.contains(key) && value is Boolean|Mapping)
.map((key, value) -> Pair(key, toJsonSchema(value, $$baseSchema, path.add(key))))
.toMapping()
}

/// Given a JSON string or [Resource], parse it into a [JsonSchema.Schema] instance.
function parse(src: Resource|String): JsonSchema.Schema =
let (jsonParsed: Mapping|Boolean = jsonParser.parse(src) as Mapping|Boolean)
toJsonSchema(jsonParsed, null, List())
72 changes: 72 additions & 0 deletions samples/Pkl/SchemaGenerator.pkl
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
module org.openapis.v3.tests.SchemaGenerator

amends "pkl:test"

import "fixtures/SelfReference.pkl"
import "../examples/SwallowSchema.pkl"
import "../SchemaGenerator.pkl"
import "../Schema.pkl"

local schema = SchemaGenerator.generate(SwallowSchema.Swallow)

facts {
["recursive references throw an error"] {
module.catch(() -> SchemaGenerator.generate(SelfReference).output.text)
.startsWith("Invalid Schema: Unable to convert a schema that refers to itself.")
}
["nullable values show up as nullable"] {
(schema.properties!!["airSpeed"] as Schema).nullable == true
}
["deprecated fields show up as deprecated"] {
(schema.properties!!["isDuck"] as Schema).deprecated == true
}
["generates basic metadata"] {
schema.title == "Swallow"
schema.description == "This is a Swallow"
}
["listings are encoded as arrays"] {
local nicknameSchema = schema.properties!!["nicknames"] as Schema
nicknameSchema.type == "array"
nicknameSchema.items.type == "string"
}
["mappings are encoded as objects"] {
local tagsSchema = schema.properties!!["tags"] as Schema
tagsSchema.type == "object"
tagsSchema.additionalProperties.type == "string"
}
["classes are encoded as objects"] {
local friendSchema = schema.properties!!["bestFriend"] as Schema
friendSchema.type == "object"
friendSchema.properties.toMap().keys == Set("name", "isSwallow", "tags")
}
}

examples {
["converts a module"] {
let (schema = new SchemaGenerator {
converters {
[DataSize] {
title = "DataSize"
type = "string"
description = "The size of data in [quantity][unit] representation."
}
}
}.generate(SwallowSchema.Swallow))
new JsonRenderer {}.renderDocument(schema)
}
}
50 changes: 50 additions & 0 deletions samples/Pkl/rule.pkl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
module io.prometheus.examples.rule

amends "../Rule.pkl"

groups {
new AlertingRuleGroup {
name = "alerting_rules"
interval = 5.min
rules {
new {
alert = "HighRequestLatency"
expr = #"job:request_latency_seconds:mean5m{job="myjob"} > 0.5"#
`for` = 10.min
labels {
["prod"] = true
["priority"] = 1
["severity"] = "page"
}
annotations {
["summary"] = "High request latency"
}
}
}
}
new RecordingRuleGroup {
name = "recording_rules"
interval = 10.h
rules {
new {
`record` = "job:http_inprogress_requests:sum"
expr = "sum by (job) (http_inprogress_requests)"
}
}
}
}
7 changes: 7 additions & 0 deletions test/test_heuristics.rb
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,13 @@ def test_php_by_heuristics
})
end

def test_pkl_by_heuristics
assert_heuristics({
"Pkl" => all_fixtures("Pkl", "*.pkl"),
"Pickle" => all_fixtures("Pickle", "*.pkl")
})
end

def test_pl_by_heuristics
assert_heuristics({
"Prolog" => all_fixtures("Prolog", "*.pl"),
Expand Down
1 change: 1 addition & 0 deletions vendor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ This is a list of grammars that Linguist selects to provide syntax highlighting
- **PigLatin:** [goblindegook/sublime-text-pig-latin](https://github.com/goblindegook/sublime-text-pig-latin)
- **Pike:** [hww3/pike-textmate](https://github.com/hww3/pike-textmate)
- **Pip Requirements:** [microsoft/vscode-python](https://github.com/microsoft/vscode-python)
- **Pkl:** [apple/pkl.tmbundle](https://github.com/apple/pkl.tmbundle)
- **PlantUML:** [qjebbs/vscode-plantuml](https://github.com/qjebbs/vscode-plantuml)
- **Pod 6:** [perl6/atom-language-perl6](https://github.com/perl6/atom-language-perl6)
- **PogoScript:** [featurist/PogoScript.tmbundle](https://github.com/featurist/PogoScript.tmbundle)
Expand Down
1 change: 1 addition & 0 deletions vendor/grammars/pkl.tmbundle
Submodule pkl.tmbundle added at ebeadf
Loading

0 comments on commit ebe6ef7

Please sign in to comment.