Skip to content

Commit

Permalink
add element support to repr, move repr to data
Browse files Browse the repository at this point in the history
  • Loading branch information
PgBiel committed Jan 13, 2025
1 parent 86b6a8b commit 22ed8da
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 60 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ pkgbase="${XDG_DATA_HOME:-$HOME/.local/share}/typst/packages/local/elembic" && m
#assert.eq(
e.repr(person("John", age: 50, preference: "soup")),
"person(age: 50, preference: \"soup\", name: \"John\")"
"person(age: 50, name: \"John\", preference: \"soup\")"
)
// Manually invoke typechecking and cast
Expand Down
77 changes: 77 additions & 0 deletions src/data.typ
Original file line number Diff line number Diff line change
Expand Up @@ -278,3 +278,80 @@
assert(false, message: "e.counter: this is not an element")
}
}

#let _letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-"

/// This is used to obtain a debug representation of custom elements and types.
///
/// Also supports native types (just calls `repr()` for them).
///
/// - value (any): value to represent
/// - depth (int): current depth (must start at 0, conservative limit of 10 for now)
/// -> str
#let repr_(value, depth: 0) = {
if depth >= 10 {
return repr(value)
}
let typename = ""
let value-type = type(value)
if value-type == content and value.func() == sequence {
let value-data = data(value)
if "eid" in value-data and value-data.eid != none {
value = value-data.fields
value-type = dictionary
typename = if "name" in value-data and type(value-data.name) == str {
value-data.name
} else {
"unknown-element"
}
}
}

if value-type == dictionary {
let pairs = if typename != "" {
// Element fields => sort
value.pairs().sorted(key: ((k, _)) => k)
} else if custom-type-key in value {
let type-data = value.at(custom-type-key)

let id = type-data.id

typename = if "name" in id {
id.name
} else if id == "custom type" {
return if custom-type-data-key in value {
"custom-type(name: " + repr(value.name) + ", tid: " + repr(value.tid) + ")"
} else {
"custom-type()"
}
} else {
str(id)
}

type-data.fields.pairs().sorted(key: ((k, _)) => k)
} else {
value.pairs()
}

typename
"("
pairs.map(((k, v)) => {
if k.codepoints().all(c => c in _letters) {
k
} else {
repr(k)
}

": "

repr_(v, depth: depth + 1)
}).join(", ")
")"
} else if value-type == array {
"("
value.map(repr_.with(depth: depth + 1)).join(", ")
")"
} else {
repr(value)
}
}
2 changes: 2 additions & 0 deletions src/element.typ
Original file line number Diff line number Diff line change
Expand Up @@ -1820,6 +1820,7 @@
func: __elembic_func,
scope: scope,
default-constructor: default-constructor,
name: name,
eid: eid,
ctx: if contextual {
(get: get-styles.with(elements: global-data.elements))
Expand Down Expand Up @@ -2025,6 +2026,7 @@
fields: args,
func: __elembic_func,
default-constructor: default-constructor,
name: name,
eid: eid,
ctx: none,
counter: element-counter,
Expand Down
1 change: 0 additions & 1 deletion src/lib.typ
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#import "element.typ": set_, data, apply, revoke, reset, named, style-modes, prepare-get as get, selector, select, ref_ as ref, prepare
#import "fields.typ": field
#import "types/base.typ": repr_ as repr
#import "pub/data.typ": *
#import "pub/element.typ"
#import "pub/parsing.typ"
Expand Down
2 changes: 1 addition & 1 deletion src/pub/data.typ
Original file line number Diff line number Diff line change
@@ -1 +1 @@
#import "../data.typ": fields, counter_ as counter, ctx, scope, func, eid, tid
#import "../data.typ": fields, counter_ as counter, ctx, scope, func, eid, tid, repr_ as repr
2 changes: 1 addition & 1 deletion src/pub/types.typ
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Public re-exports for type-related functions and constants.
#import "../types/base.typ": ok, err, is-ok, any, never, custom-type, typeid, typename, repr_ as repr
#import "../types/base.typ": ok, err, is-ok, any, never, custom-type, typeid, typename
#import "../types/types.typ": option, smart, union, paint, literal, exact, wrap, array_ as array, default, validate as typeinfo, cast, generate-cast-error
#import "../types/custom.typ": declare
#import "native.typ"
52 changes: 1 addition & 51 deletions src/types/base.typ
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// The shared fundamentals of the type system.
#import "../data.typ": data, type-key, custom-type-key, custom-type-data-key
#import "../data.typ": data, type-key, custom-type-key, custom-type-data-key, repr_

#let type-version = 1

Expand Down Expand Up @@ -104,56 +104,6 @@
)
}

#let _letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-"

/// This is used to obtain a debug representation of custom types.
///
/// In the future, this will support elements as well.
///
/// Also supports native types (just calls `repr()` for them).
#let repr_(value, depth: 0) = {
if depth < 10 and type(value) == dictionary {
let dict = value
if custom-type-key in value {
let type-data = value.at(custom-type-key)
dict = type-data.fields

let id = type-data.id
if "name" in id {
id.name
} else if id == "custom type" {
return if custom-type-data-key in value {
"custom-type(name: " + repr(value.name) + "', tid: " + repr(value.tid) + ")"
} else {
"custom-type()"
}
} else {
str(id)
}
}

"("
dict.pairs().map(((k, v)) => {
if k.codepoints().all(c => c in _letters) {
k
} else {
repr(k)
}

": "

repr_(v, depth: depth + 1)
}).join(", ")
")"
} else if depth < 10 and type(value) == array {
"("
value.map(repr_.with(depth: depth + 1)).join(", ")
")"
} else {
repr(value)
}
}

// Literal type
// Only accepted if value is equal to the literal.
// Input and output are equal to the value.
Expand Down
46 changes: 46 additions & 0 deletions test/integration/readme.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#import "/src/lib.typ" as e: field, types

#[
#let bigbox = e.element.declare(
"bigbox",
prefix: "@preview/my-package,v1",
display: it => block(fill: it.fill, stroke: it.stroke, inset: 5pt, it.body),
fields: (
field("body", types.option(content), doc: "Box contents", required: true),
field("fill", types.option(types.paint), doc: "Box fill"),
field("stroke", types.option(stroke), doc: "Box border", default: red)
)
)

#bigbox[abc]

#show: e.set_(bigbox, fill: red)

#bigbox(stroke: blue + 2pt)[def]
]

#[
#let person = e.types.declare(
"person",
prefix: "@preview/my-package,v1",
fields: (
field("name", str, doc: "Person's name", required: true),
field("age", int, doc: "Person's age", default: 40),
field("preference", types.any, doc: "Anything the person likes", default: none)
),
casts: (
(from: str, with: person => name => person(name)),
)
)

#assert.eq(
e.repr(person("John", age: 50, preference: "soup")),
"person(age: 50, name: \"John\", preference: \"soup\")"
)

// Manually invoke typechecking and cast
#assert.eq(
types.cast("abc", person),
(true, person("abc"))
)
]
10 changes: 8 additions & 2 deletions test/unit/elements/misc-data/test.typ
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
display: it => {},
fields: (
field("color", color, default: red),
field("border", stroke, default: red),
field("border", stroke, default: blue),
field("inner", content, default: [Hello!])
),
prefix: ""
Expand All @@ -19,6 +19,12 @@
assert.eq(e.func(it), e.data(it).func)
assert.eq(e.eid(it), e.eid(wock))
assert.eq(e.eid(it), e.data(it).eid)

assert.eq(e.repr(it), "wock(border: luma(0%), color: rgb(\"#ff4136\"), inner: [Hello!])")
}

#wock()
#let w = wock(border: black)

#assert.eq(e.repr(w), "wock(border: luma(0%))")

#w
2 changes: 1 addition & 1 deletion test/unit/types/custom/literal/test.typ
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@

#assert.eq(cast(person("John", 100), types.union(types.literal(person("John", 100)), types.literal(person("John", 200)))), (true, person("John", 100)))
#assert.eq(cast(person("John", 200), types.union(types.literal(person("John", 100)), types.literal(person("John", 200)))), (true, person("John", 200)))
#assert.eq(cast(person("John", 101), types.union(types.literal(person("John", 100)), types.literal(person("John", 200)))), (false, "given value wasn't equal to literals 'person(border: 5pt, name: \"John\", age: 100)' or 'person(border: 5pt, name: \"John\", age: 200)'"))
#assert.eq(cast(person("John", 101), types.union(types.literal(person("John", 100)), types.literal(person("John", 200)))), (false, "given value wasn't equal to literals 'person(age: 100, border: 5pt, name: \"John\")' or 'person(age: 200, border: 5pt, name: \"John\")'"))
2 changes: 1 addition & 1 deletion test/unit/types/custom/misc-data/test.typ
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
prefix: "mypkg"
)

#assert.eq(e.repr(person("John", 5)), "person(border: 5pt, name: \"John\", age: 5)")
#assert.eq(e.repr(person("John", 5)), "person(age: 5, border: 5pt, name: \"John\")")
#assert.eq(e.tid(person), e.data(person).tid)
#assert.eq(e.tid(person("John", 5)), e.tid(person))
#assert.eq(e.func(person), person)
Expand Down
2 changes: 1 addition & 1 deletion test/unit/types/custom/type-of-type/test.typ
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@
#assert.eq(cast(e.data(sides(red)), dictionary), (false, "expected dictionary, found custom type"))
#assert.eq(cast(e.data(sides(red)), types.union(dictionary, stroke, types.custom-type)), (true, e.data(sides(red))))
#assert.eq(cast(e.data(sides(red)), types.literal(e.data(sides(red)))), (true, e.data(sides(red))))
#assert.eq(cast(e.data(sides(blue)), types.literal(e.data(sides(red)))), (false, "given value wasn't equal to literal 'custom-type(name: \"sides of literal 'rgb(\\\"#ff4136\\\")'\"', tid: \"t__---_sides of literal 'rgb(\\\"#ff4136\\\")'\")'"))
#assert.eq(cast(e.data(sides(blue)), types.literal(e.data(sides(red)))), (false, "given value wasn't equal to literal 'custom-type(name: \"sides of literal 'rgb(\\\"#ff4136\\\")'\", tid: \"t__---_sides of literal 'rgb(\\\"#ff4136\\\")'\")'"))

0 comments on commit 22ed8da

Please sign in to comment.