Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 144 additions & 0 deletions dsl/extdeps/languages/json/emit.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
module extdeps.languages.json.emit

import extdeps.external_authority { ExternalAuthority }
import extdeps.languages.json.grammar { JsonNumberLexeme, json_int_lexeme }
import extdeps.uri { Uri, Https }

data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority {
uri: Uri {
scheme: Https
locator: "www.rfc-editor.org/rfc/rfc8259"
}
}

type JsonKeyValue {
key: String
value: JsonValue
}

type JsonValue
= JsonNull
| JsonBool { value: Bool }
| JsonNumber { lexeme: JsonNumberLexeme }
| JsonString { value: String }
| JsonArray { elements: List<JsonValue> }
| JsonObject { members: List<JsonKeyValue> }

fn json_kv(key: String, value: JsonValue) -> JsonKeyValue {
JsonKeyValue { key: key, value: value }
}

fn json_null() -> JsonValue {
JsonNull
}

fn json_bool(b: Bool) -> JsonValue {
JsonBool { value: b }
}

fn json_int(n: Int) -> JsonValue {
JsonNumber { lexeme: json_int_lexeme(n: n) }
}

fn json_string(s: String) -> JsonValue {
JsonString { value: s }
}

fn json_array(elements: List<JsonValue>) -> JsonValue {
JsonArray { elements: elements }
}

fn json_object(members: List<JsonKeyValue>) -> JsonValue {
JsonObject { members: members }
}

fn hex_digit_char(d: Int) -> String {
if d < 10 { from_code_point(cp: 48 + d) }
else { from_code_point(cp: 55 + d) }
}

fn int_to_upper_hex_inner(n: Int, acc: String) -> String {
if n == 0 { acc }
else { int_to_upper_hex_inner(n: n / 16, acc: concat(hex_digit_char(d: n % 16), acc)) }
}

fn int_to_upper_hex(n: Int) -> String {
if n == 0 { "0" }
else { int_to_upper_hex_inner(n: n, acc: "") }
}

fn json_unicode_escape_hex4(cp: Int) -> String {
let h = int_to_upper_hex(n: cp)
let len = h.length()
if len == 1 { concat("000", h) }
else if len == 2 { concat("00", h) }
else if len == 3 { concat("0", h) }
else { h }
}

fn json_escape_code_point(cp: Int) -> String {
if cp == 8 { "\\b" }
else if cp == 9 { "\\t" }
else if cp == 10 { "\\n" }
else if cp == 12 { "\\f" }
else if cp == 13 { "\\r" }
else if cp == 34 { "\\\"" }
else if cp == 92 { "\\\\" }
else if cp >= 0 && cp <= 31 { concat("\\u", json_unicode_escape_hex4(cp: cp)) }
else { from_code_point(cp: cp) }
}

fn escape_json_string(s: String) -> String {
fold(chars(s: s), init: "", f: (acc, cp) => concat(acc, json_escape_code_point(cp: cp)))
}

fn quote_json_string(s: String) -> String {
concat("\"", concat(escape_json_string(s: s), "\""))
}

fn serialize_json_string(s: String) -> String {
quote_json_string(s: s)
}

fn join_json_values(items: List<JsonValue>) -> String {
fold(items, init: "", f: (acc, v) => {
let part = serialize_json(v: v)
if acc == "" { part } else { concat(acc, concat(", ", part)) }
})
}

fn serialize_json_array(elements: List<JsonValue>) -> String {
concat("[", concat(join_json_values(items: elements), "]"))
}

fn serialize_json_object(members: List<JsonKeyValue>) -> String {
concat(
"{",
concat(
fold(members, init: "", f: (acc, kv) => {
let part = concat(concat(quote_json_string(s: kv.key), ": "), serialize_json(v: kv.value))
if acc == "" { part } else { concat(acc, concat(", ", part)) }
}),
"}"
)
)
}

fn serialize_json(v: JsonValue) -> String {
match v {
JsonNull => "null"
JsonBool { value: b } => if b { "true" } else { "false" }
JsonNumber { lexeme: l } => l
JsonString { value: s } => quote_json_string(s: s)
JsonArray { elements: els } => serialize_json_array(elements: els)
JsonObject { members: m } => serialize_json_object(members: m)
}
}

fn json_int_list(nums: List<Int>) -> String {
serialize_json_array(elements: nums |> map(n => json_int(n: n)))
}

fn json_str_list(items: List<String>) -> String {
serialize_json_array(elements: items |> map(s => json_string(s: s)))
}
149 changes: 149 additions & 0 deletions dsl/extdeps/languages/json/grammar.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
module extdeps.languages.json.grammar

import std.types { String }
import extdeps.external_authority { ExternalAuthority }
import extdeps.uri { Uri, Https }

data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority {
uri: Uri {
scheme: Https
locator: "www.rfc-editor.org/rfc/rfc8259"
}
}

type JsonProductionRow {
rfc_section: String
lhs: String
rhs: String
value_variant: String
}

data json_production_rows: List<JsonProductionRow> = [
JsonProductionRow { rfc_section: "3", lhs: "JSON-text", rhs: "ws value ws", value_variant: "JsonValue" },
JsonProductionRow { rfc_section: "3", lhs: "value", rhs: "object | array | string | number | literal", value_variant: "JsonValue" },
JsonProductionRow { rfc_section: "3", lhs: "object", rhs: "'{' ws member (',' ws member)* '}' | '{' ws '}'", value_variant: "JsonObject" },
JsonProductionRow { rfc_section: "3", lhs: "member", rhs: "string ws ':' ws value", value_variant: "JsonKeyValue" },
JsonProductionRow { rfc_section: "3", lhs: "array", rhs: "'[' ws value (',' ws value)* ']' | '[' ws ']'", value_variant: "JsonArray" },
JsonProductionRow { rfc_section: "3", lhs: "string", rhs: "'\"' char* '\"'", value_variant: "JsonString" },
JsonProductionRow { rfc_section: "3", lhs: "number", rhs: "minus? int frac? exp?", value_variant: "JsonNumber" },
JsonProductionRow { rfc_section: "3", lhs: "literal", rhs: "'null' | 'true' | 'false'", value_variant: "JsonNull|JsonBool" },
]

data json_emit_fold_dissolution_trigger: String = "dissolve-on: v2 grammar-inverse ingest for JSON — dsl/extdeps/languages/json/grammar.dag plus emit.dag is the single dsl-tree authority (RFC 8259 rows + JsonValue fold); DISSOLVES WHEN src/v2/extdeps/languages/json.dag lands FormalProduction/TargetModel rows wired through 06_translate with ingest o emit = id on json_production_rows witnesses"

type JsonNumberLexeme = String where brand("JsonNumberLexeme")

type JsonNumberParseRest {
rest: String
ok: Bool
}

fn is_json_digit_char(c: String) -> Bool {
(c != "") && string_contains(s: "0123456789", pattern: c)
}

fn is_json_digit1_char(c: String) -> Bool {
(c != "") && string_contains(s: "123456789", pattern: c)
}

fn json_number_consume_digit_run(s: String) -> JsonNumberParseRest {
if s.length() == 0 {
JsonNumberParseRest { rest: "", ok: true }
} else {
let first = char_at(s, 0)
if !is_json_digit_char(c: first) {
JsonNumberParseRest { rest: s, ok: true }
} else {
let inner = json_number_consume_digit_run(s: substring(s: s, start: 1, end: s.length()))
JsonNumberParseRest { rest: inner.rest, ok: inner.ok }
}
}
}

fn json_number_consume_int(s: String) -> JsonNumberParseRest {
if s.length() == 0 {
JsonNumberParseRest { rest: s, ok: false }
} else if char_at(s, 0) == "0" {
JsonNumberParseRest { rest: substring(s: s, start: 1, end: s.length()), ok: true }
} else if !is_json_digit1_char(c: char_at(s, 0)) {
JsonNumberParseRest { rest: s, ok: false }
} else {
json_number_consume_digit_run(s: s)
}
}

fn json_number_consume_frac_digits(s: String) -> JsonNumberParseRest {
if s.length() == 0 {
JsonNumberParseRest { rest: "", ok: false }
} else {
let first = char_at(s, 0)
if !is_json_digit_char(c: first) {
JsonNumberParseRest { rest: s, ok: false }
} else {
json_number_consume_digit_run(s: s)
}
}
}

fn json_number_consume_optional_frac(s: String) -> JsonNumberParseRest {
if s.length() == 0 {
JsonNumberParseRest { rest: "", ok: true }
} else if char_at(s, 0) != "." {
JsonNumberParseRest { rest: s, ok: true }
} else {
let after = substring(s: s, start: 1, end: s.length())
let parsed = json_number_consume_frac_digits(s: after)
JsonNumberParseRest { rest: parsed.rest, ok: parsed.ok }
}
}

fn json_number_consume_optional_exp(s: String) -> Bool {
if s.length() == 0 {
true
} else {
let marker = char_at(s, 0)
if marker != "E" && marker != "e" {
false
} else {
let after = substring(s: s, start: 1, end: s.length())
let signed = if after.length() > 0 && (char_at(after, 0) == "+" || char_at(after, 0) == "-") {
substring(s: after, start: 1, end: after.length())
} else {
after
}
let parsed = json_number_consume_frac_digits(s: signed)
parsed.ok && parsed.rest.length() == 0
}
}
}

fn json_number_lexeme_valid(s: String) -> Bool {
if s.length() == 0 {
false
} else {
let body = if starts_with(s: s, prefix: "-") {
substring(s: s, start: 1, end: s.length())
} else {
s
}
if starts_with(s: s, prefix: "-") && body.length() == 0 {
false
} else {
let int_parsed = json_number_consume_int(s: body)
if !int_parsed.ok {
false
} else {
let frac_parsed = json_number_consume_optional_frac(s: int_parsed.rest)
if !frac_parsed.ok {
false
} else {
json_number_consume_optional_exp(s: frac_parsed.rest)
}
}
}
}
}

fn json_int_lexeme(n: Int) -> JsonNumberLexeme {
to_string(n) as JsonNumberLexeme
}
Loading
Loading