diff --git a/src/cg/03-ui-components.cg b/src/cg/03-ui-components.cg index 0441d3c..ac64bef 100644 --- a/src/cg/03-ui-components.cg +++ b/src/cg/03-ui-components.cg @@ -390,58 +390,60 @@ LayoutChild = Fixed Int (Int \ Int \ UI) | Flex Int (Int \ Int \ UI); Alignment = Start | Center | End; Direction = Col | Row; -layoutDir - : Direction \ { w : Int, h : Int } \ List LayoutChild \ { ui : UI, w : Int, h : Int } - = dir constraints children \ - mainSize = dir | Col \ constraints.h | Row \ constraints.w; - crossSize = dir | Col \ constraints.w | Row \ constraints.h; +layout = { + layoutDir + # : Direction \ { w : Int, h : Int } \ List LayoutChild \ { ui : UI, w : Int, h : Int } + = dir constraints children \ + mainSize = dir | Col \ constraints.h | Row \ constraints.w; + crossSize = dir | Col \ constraints.w | Row \ constraints.h; - # Pass 1: sum fixed heights, total flex weight - info = fold (acc child \ child - | Fixed s _ \ acc.{ fixedMain = acc.fixedMain + s } - | Flex n _ \ acc.{ totalFlex = acc.totalFlex + n } - ) { fixedMain = 0, totalFlex = 0 } children; + # Pass 1: sum fixed heights, total flex weight + info = fold (acc child \ child + | Fixed s _ \ acc.{ fixedMain = acc.fixedMain + s } + | Flex n _ \ acc.{ totalFlex = acc.totalFlex + n } + ) { fixedMain = 0, totalFlex = 0 } children; - remaining = mainSize - info.fixedMain; + remaining = mainSize - info.fixedMain; - # Pass 2 - result = fold (acc child \ - allocated = (child - | Fixed s view \ { s = s, view = view } - | Flex n view \ { s = remaining * n / info.totalFlex, view = view }); + # Pass 2 + result = fold (acc child \ + allocated = (child + | Fixed s view \ { s = s, view = view } + | Flex n view \ { s = remaining * n / info.totalFlex, view = view }); - childW = dir | Col \ crossSize | Row \ allocated.s; - childH = dir | Col \ allocated.s | Row \ crossSize; - posX = dir | Col \ 0 | Row \ acc.pos; - posY = dir | Col \ acc.pos | Row \ 0; + childW = dir | Col \ crossSize | Row \ allocated.s; + childH = dir | Col \ allocated.s | Row \ crossSize; + posX = dir | Col \ 0 | Row \ acc.pos; + posY = dir | Col \ acc.pos | Row \ 0; - rendered = ui.positioned { - x = posX, y = posY, - child = allocated.view childW childH - }; + rendered = ui.positioned { + x = posX, y = posY, + child = allocated.view childW childH + }; - acc.{ pos = acc.pos + allocated.s, children = [...acc.children, rendered] } - ) { pos = 0, children = [] } children; + acc.{ pos = acc.pos + allocated.s, children = [...acc.children, rendered] } + ) { pos = 0, children = [] } children; - { ui = ui.stack { children = result.children }, w = constraints.w, h = constraints.h }; + { ui = ui.stack { children = result.children }, w = constraints.w, h = constraints.h }, -col = layoutDir Col; -row = layoutDir Row; + col = layoutDir Col, + row = layoutDir Row, -align = hAlign vAlign w h child \ - x = hAlign - | Start \ 0 - | Center \ (w - child.w) / 2 - | End \ w - child.w; - y = vAlign - | Start \ 0 - | Center \ (h - child.h) / 2 - | End \ h - child.h; - { ui = ui.positioned { x = x, y = y, child = child.ui }, w = w, h = h }; + align = hAlign vAlign w h child \ + x = hAlign + | Start \ 0 + | Center \ (w - child.w) / 2 + | End \ w - child.w; + y = vAlign + | Start \ 0 + | Center \ (h - child.h) / 2 + | End \ h - child.h; + { ui = ui.positioned { x = x, y = y, child = child.ui }, w = w, h = h }, +}; testApp = name \ { view = ctx \ - col { w = ctx.w, h = ctx.h } [ + layout.col { w = ctx.w, h = ctx.h } [ Fixed 40 (w h \ box { w = w, h = h, color = "red", child = text "Header" }), Flex 1 (w h \ box { w = w, h = h, color = "#1a1a2e", child = text "Main content" }), Fixed 30 (w h \ box { w = w, h = h, color = "blue", child = text "Footer" }), @@ -451,13 +453,13 @@ testApp = name \ testApp2 = _ \ { view = ctx \ - col { w = ctx.w, h = ctx.h } [ - Fixed 40 (w h \ (align Center Center w h (sizedText "Header")).ui), - Flex 1 (w h \ (row { w = w, h = h } [ + layout.col { w = ctx.w, h = ctx.h } [ + Fixed 40 (w h \ (layout.align Center Center w h (sizedText "Header")).ui), + Flex 1 (w h \ (layout.row { w = w, h = h } [ Fixed 200 (w h \ box { w = w, h = h, color = "#2a2a3e", child = text "Sidebar" }), Flex 1 (w h \ box { w = w, h = h, color = "#1a1a2e", child = text "Main" }), ]).ui), - Fixed 30 (w h \ (align Center Center w h (sizedText "Footer")).ui), + Fixed 30 (w h \ (layout.align Center Center w h (sizedText "Footer")).ui), ] ~ (e \ e.ui) }; diff --git a/src/compiler.ts b/src/compiler.ts index 66bbed6..f75e169 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -69,11 +69,36 @@ export function compile(ast: AST, ctx: CompileCtx = defaultCtx): string { return `${compile(ast.func, ctx)}(${args})`; case 'record': { + const fieldNames = new Set( + ast.entries.filter(e => e.kind === 'field').map(e => e.key) + ); + + // Check if any field references another + const needsHoist = ast.entries.some(entry => + entry.kind === 'field' && + [...freeVars(entry.value)].some(v => + fieldNames.has(v) && v !== entry.key && !ctx.bound.has(v) && !ctx.topLevel.has(v) + ) + ); + + if (needsHoist) { + const bindings: string[] = []; + const names: string[] = []; + for (const entry of ast.entries) { + if (entry.kind === 'spread') continue; + const safe = sanitizeName(entry.key); + bindings.push(`const ${safe} = ${compile(entry.value, { ...ctx, bound: new Set([...ctx.bound, ...fieldNames]) })};`); + names.push(`${JSON.stringify(entry.key)}: ${safe}`); + } + return `(() => { ${bindings.join(' ')} return {${names.join(', ')}}; })()`; + } + + // Simple record, no hoisting const parts = ast.entries.map(entry => - entry.kind === 'spread' - ? `...${compile(entry.expr, ctx)}` - : `${JSON.stringify(entry.key)}: ${compile(entry.value, ctx)}` - ) + entry.kind === 'spread' + ? `...${compile(entry.expr, ctx)}` + : `${JSON.stringify(entry.key)}: ${compile(entry.value, ctx)}` + ) return `({${parts.join(', ')}})`; }