Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map struct encoding #129

Closed
wants to merge 4 commits into from
Closed
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
2 changes: 1 addition & 1 deletion jsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"target": "esnext",
"lib": ["es2020"]
},
"exclude": ["node_modules", "**/*.d.ts", "test"],
"exclude": ["node_modules", "types/**/*.d.ts", "test"],
"include": [
"src/**/*.js",
"node_modules/@helios-lang/type-utils",
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
"build:types": "tsc -p jsconfig.json --noEmit false --emitDeclarationOnly",
"lockfile:sync": "pnpm install --ignore-workspace",
"prettify": "prettier . --write",
"pretest": "pnpm test:version && pnpm run test:pretty",
"pretest": "./.pre-push-hook install && pnpm test:version && pnpm run test:pretty",
"test": "pnpm run test:types && pnpm run test:suite",
"test:pretty": "prettier . --check",
"test:suite": "node --stack-trace-limit=50 --test",
"test:types": "tsc -p jsconfig.json --noEmit",
"test:version": "node -e \"import('./src/index.js').then(m => {if (m.VERSION != process.env.npm_package_version) {throw new Error(\\\"version mismatch\\\")}})\"",
"testing": "node --stack-trace-limit=50 --test --watch",
"testing:debug": "node --inspect-brk --stack-trace-limit=50 --test-concurrency=1 --test --watch"
"testing": "HL_TEST_TRACE=ok node --stack-trace-limit=50 --test --watch",
"testing:debug": "HL_TEST_TRACE=ok node --stack-trace-limit=50 --inspect-wait --test --watch"
},
"repository": {
"type": "git",
Expand Down
61 changes: 28 additions & 33 deletions src/codegen/makeRawFuncs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { REAL_PRECISION } from "@helios-lang/compiler-utils"
import { expectSome } from "@helios-lang/type-utils"
import { FTPP, TTPP } from "./ParametricName.js"
import { RawFunc } from "./RawFunc.js"
import { $ } from "@helios-lang/ir"

/**
* Initializes the db containing all the builtin functions
Expand Down Expand Up @@ -685,30 +686,33 @@ export function makeRawFunctions(simplify, isTestnet) {
)
add(
new RawFunc(
"__helios__common__cip68_field",
"__helios__common__mStruct_field",
`(self, name) -> {
map = __core__unMapData(__core__headList(__core__sndPair(__core__unConstrData(self))));
__helios__common__cip68_field_internal(map, name)
__helios__common__mStruct_field_internal(
__core__unMapData(self),
name
)
}`
)
)
// map is expected to already have been extracted
// map's underlying list was already extracted
add(
new RawFunc(
"__helios__common__cip68_field_internal",
"__helios__common__mStruct_field_internal",
`(map, name) -> {
name_data = __core__bData(name);
recurse = (recurse, map) -> {
__core__chooseList(
map,
() -> {
__helios__error(
__core__appendString(
__core__appendString(
"field ",
__core__appendString(
__helios__bytearray__show(name)(),
" not found in struct"
)
__core__decodeUtf8(__core__unBData(name)),
// __core__decodeUtf8(__core__unBData__safe(name)),
" not found in mStruct"
)
)
)
},
Expand All @@ -735,7 +739,7 @@ export function makeRawFunctions(simplify, isTestnet) {
// map is expected to already have been extracted
add(
new RawFunc(
"__helios__common__cip68_field_safe",
"__helios__common__mStruct_field_safe",
`(map, name) -> {
name = __core__bData(name);
recurse = (recurse, map) -> {
Expand Down Expand Up @@ -763,28 +767,27 @@ export function makeRawFunctions(simplify, isTestnet) {
}`
)
)
//checks for the presence of a field in an mStruct (not its inner list)
add(
new RawFunc(
"__helios__common__test_cip68_field",
"__helios__common__test_mStruct_field",
`(self, name, inner_test) -> {
__core__chooseData(
self,
() -> {false},
() -> {
fields = __core__sndPair(__core__unConstrData__safe(self));
__core__chooseList(
fields,
() -> {false},
() -> {
head = __core__headList__safe(fields);
__core__chooseData(
head,
() -> {false},
() -> {
map = __core__unMapData__safe(head);
map = __core__unMapData__safe(self);
recurse = (recurse, map) -> {
__core__chooseList(
map,
() -> {false},
__core__chooseList(map,
() -> {
__core__trace(
__core__appendString(
"Warning: field not found in mStruct: ",
__core__decodeUtf8(__core__unBData(name))
),
() -> {false}
)()
},
() -> {
head = __core__headList__safe(map);
key = __core__fstPair(head);
Expand All @@ -802,17 +805,9 @@ export function makeRawFunctions(simplify, isTestnet) {
)()
};
recurse(recurse, map)
},
() -> {false},
() -> {false},
() -> {false}
)()
}
)()
},
() -> {false},
() -> {false},
() -> {false},
() -> {false}
)()
}`
Expand Down
23 changes: 20 additions & 3 deletions src/expressions/Expr.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,17 @@ export class Expr {
cache

/**
* @param {Site} site

*/
* @type {string | null}
*/
encodingKey

/**
* @param {Site} site
*/
constructor(site) {
this.site = site
this.cache = None
this.encodingKey = null
}

/**
Expand All @@ -58,11 +63,22 @@ export class Expr {
return this.cache
}

/**
* Annotates the expression with a serialization encoding-key (only relevant for types used in fields of Map-typed (CIP-68) structs
* @param {string} key
* @returns {Expr}
*/
withEncodingKey(key) {
this.encodingKey = key
return this
}

/**
* @param {Scope} scope
* @returns {DataType}
*/
evalAsDataType(scope) {
// here with the field-name tag??
const result_ = this.eval(scope)

const result = result_.asDataType
Expand All @@ -80,6 +96,7 @@ export class Expr {
*/
evalAsType(scope) {
const r = this.eval(scope)
const result_ = this.eval(scope)

const result = r.asType

Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@
//

export * from "./program/index.js"
export * as testUtils from "../test/utils.js"
57 changes: 35 additions & 22 deletions src/parse/parseDataFields.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import { parseTypeExpr } from "./parseTypeExpr.js"

/**
* @param {ParseContext} ctx
* @param {boolean} allowTags
* @param {boolean} allowEncodingKeys
* @returns {DataField[]}
*/
export function parseDataFields(ctx, allowTags = false) {
export function parseDataFields(ctx, allowEncodingKeys = false) {
const r = ctx.reader

/**
Expand All @@ -31,7 +31,7 @@ export function parseDataFields(ctx, allowTags = false) {
/**
* @type {Map<string, StringLiteral>}
*/
const tags = new Map()
const encodingKeys = new Map()

/**
* @param {Word} fieldName
Expand All @@ -51,26 +51,26 @@ export function parseDataFields(ctx, allowTags = false) {

/**
* @param {Site} site
* @param {string} tag
* @param {string} key
*/
function assertUniqueTag(site, tag) {
if (tags.has(tag)) {
ctx.errors.syntax(site, `duplicate tag '${tag}'`)
function assertUniqueEncodingKey(site, key) {
if (encodingKeys.has(key)) {
ctx.errors.syntax(site, `duplicate encoding-key '${key}'`)
}
}

/**
* @param {Word} fieldName
* @returns {Option<StringLiteral>}
*/
function getTag(fieldName) {
const tag = tags.get(fieldName.value)
function getFieldEncodingKey(fieldName) {
const fieldKey = encodingKeys.get(fieldName.value)

if (tag) {
return tag
} else if (tags.size == 0) {
if (fieldKey) {
return fieldKey
} else if (encodingKeys.size == 0) {
return None
} else if (tags.has(fieldName.value)) {
} else if (encodingKeys.has(fieldName.value)) {
ctx.errors.syntax(
fieldName.site,
`duplicate tag '${fieldName.value}' (created implicitly)`
Expand All @@ -89,6 +89,9 @@ export function parseDataFields(ctx, allowTags = false) {

let typeReader = r.readUntil(anyName, symbol(":"))

/* @type{string} */
let encodingKey

if ((m = typeReader.findLastMatch(strlit()))) {
/**
* @satisfies {[TokenReader, StringLiteral]}
Expand All @@ -97,26 +100,36 @@ export function parseDataFields(ctx, allowTags = false) {
typeReader.end()
typeReader = before

if (!allowTags) {
ctx.errors.syntax(tag.site, "unexpected tag")
if (!allowEncodingKeys) {
ctx.errors.syntax(
tag.site,
"encodingKey tag not valid in this context"
)
} else {
assertUniqueTag(tag.site, tag.value)
assertUniqueEncodingKey(tag.site, tag.value)
}

tags.set(name.value, tag)
encodingKey = tag.value
encodingKeys.set(name.value, tag)
} else {
r.endMatch(false)

if (tags.size > 0) {
assertUniqueTag(name.site, name.value)
if (encodingKeys.size > 0) {
assertUniqueEncodingKey(name.site, name.value)
}
}

const typeExpr = parseTypeExpr(
ctx.atSite(colon.site).withReader(typeReader)
)

fields.push(new DataField(name, typeExpr, getTag(name)))
fields.push(
new DataField(
name,
encodingKey
? typeExpr.withEncodingKey(encodingKey)
: typeExpr,
getFieldEncodingKey(name)
)
)
} else {
r.endMatch()
r.end()
Expand Down
Loading
Loading