diff --git a/src/ast.ts b/src/ast.ts index 36cb678..e862a75 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -173,13 +173,6 @@ export type AST = | Definition | Rebind -const infixOps: { [key: string]: string } = { - cat: '&', add: '+', sub: '-', mul: '*', div: '/', mod: '%', - pow: '^', eq: '==', neq: '!=', gt: '>', lt: '<', gte: '>=', lte: '<=' -}; - -const isInfix = (a: AST) => a.kind === 'apply' && a.func.kind === 'variable' && a.args.length === 2 && infixOps[a.func.name]; - export function prettyPrint(ast: AST, indent = 0): string { const i = ' '.repeat(indent); @@ -202,43 +195,23 @@ export function prettyPrint(ast: AST, indent = 0): string { return ast.name; case 'apply': - // infix ops - if (isInfix(ast)) { - const wrapIfNeeded = (a: AST) => { - const printed = prettyPrint(a, indent); - if (a.kind === 'apply' || a.kind === 'lambda' || a.kind === 'match' || a.kind === 'let') { - return `(${printed})`; - } - return `${printed}`; - - } - const left = wrapIfNeeded(ast.args[0]); - const right = wrapIfNeeded(ast.args[1]); - return `${left} ${infixOps[(ast.func as Variable).name]} ${right}`; - } - const func = prettyPrint(ast.func, indent); const args = ast.args.map(a => { const printed = prettyPrint(a, indent); - if (a.kind === 'lambda' || a.kind === 'match' || a.kind === 'let' || a.kind === 'rebind' || a.kind === 'apply') { + if (a.kind === 'lambda' || a.kind === 'match' || a.kind === 'let' || a.kind === 'rebind') { return `(${printed})`; } return printed; }).join(' '); - if (ast.func.kind === 'lambda' || ast.func.kind === 'match' || ast.func.kind === 'let') { - return `(${func} ${args})` - } - return `${func} ${args}` + return `(${func} ${args})` case 'let': - const sep = indent === 0 ? '\n\n' : '\n'; - return `${ast.name} = ${prettyPrint(ast.value, indent + 1)};${sep}${i}${prettyPrint(ast.body, indent)}` + return `${ast.name} = ${prettyPrint(ast.value, indent + 1)};\n${i}${prettyPrint(ast.body, indent)}` case 'list': { const elems = ast.elements.map(e => prettyPrint(e, indent + 1)) - const oneLine = `[${elems.join(', ')}]`; - if (oneLine.length <= 60 || elems.length <= 1) return oneLine; + if (elems.length <= 1) return `[${elems.join(', ')}]`; const inner = elems.map(e => `${' '.repeat(indent + 1)}${e}`).join(',\n'); return `[\n${inner}\n${i}]`; } @@ -258,8 +231,8 @@ export function prettyPrint(ast: AST, indent = 0): string { const body = prettyPrint(ast.body, indent + 1); const isComplex = ast.body.kind === 'match' || ast.body.kind === 'let'; if (isComplex) { - const ii = ' '.repeat(indent + 1); - return `${params} \\\n${ii}${body}` + return `${params} \\\n${body}` + } return `${params} \\ ${body}` } @@ -278,11 +251,8 @@ export function prettyPrint(ast: AST, indent = 0): string { case 'match': const expr = prettyPrint(ast.expr, indent); const cases = ast.cases - .map(c => { - const result = prettyPrint(c.result, indent + 1); - const needsParens = c.result.kind === 'match' || c.result.kind === 'let'; - return `${i}| ${prettyPrintPattern(c.pattern)} \\ ${needsParens ? '(' : ''}${result}${needsParens ? ')' : ''}`; - }).join('\n'); + .map(c => `${i}| ${prettyPrintPattern(c.pattern)} \\ ${prettyPrint(c.result, indent + 1)}`) + .join('\n'); return `${expr}\n${cases}`; case 'rebind': diff --git a/src/cg/01-stdlib.cg b/src/cg/01-stdlib.cg index b05de06..cfb63bf 100644 --- a/src/cg/01-stdlib.cg +++ b/src/cg/01-stdlib.cg @@ -77,16 +77,6 @@ join = s list \ list | [x] \ x | [x, ...xs] \ (x & s & (join s xs)); -# repeatStr : Int \ String \ String -repeatStr = n str \ n - | 0 \ "" - | m \ str & (repeatStr (m - 1) str); - -# repeat : Int \ a \ List a -repeat = n a \ n - | 0 \ [] - | m \ [a, ...repeat (m - 1) a]; - # zipWith : (a \ b \ c) \ List a \ List b \ List c zipWith = f l1 l2 \ l1 | [] \ [] diff --git a/src/cg/03-ui-components.cg b/src/cg/03-ui-components.cg index d91ce72..5ca8f0e 100644 --- a/src/cg/03-ui-components.cg +++ b/src/cg/03-ui-components.cg @@ -134,8 +134,8 @@ scrollable = config \ | False \ []), ...(showHBar | True \ [ui.positioned { - x = hBarX, - y = c.h - 4, + x = c.h - 4, + y = hBarX, child = ui.rect { h = 4, w = hBarWidth, color = "rgba(255,255,255,0.3)", radius = 2 } }] | False \ []) diff --git a/src/cg/06-textEditor.cg b/src/cg/06-textEditor.cg index d503538..9540e8f 100644 --- a/src/cg/06-textEditor.cg +++ b/src/cg/06-textEditor.cg @@ -1,52 +1,10 @@ -textEditorBuffers = []; textEditor = name \ # defaults = {}; # c = { ...defaults, ...config }; - scale = 2; - charH = 12; - charW = 5; - lineGap = 1; - charGap = 2; - lineH = charH * scale + lineGap; - - buffersKey = "textEditorBuffers"; - - # load from staging buffers if it exists there. if not, load from source - source = getAt [buffersKey, name] - | None \ getSource name - | Some v \ v; + source = getSource name; lines = split "\n" source; - clampCursor = state \ - line = nth state.cursorRow state.lines ~ unwrapOr ""; - - newRow = max 0 state.cursorRow; - newRow2 = min (len state.lines - 1) newRow; - - maxCol = state.mode - | Insert \ len line - | Normal \ max 0 (len line - 1); - newCol2 = min maxCol (max 0 state.cursorCol); - - state.{ cursorRow = newRow2, cursorCol = newCol2 }; - - write = state \ - content = join "\n" state.lines; - { state = state, emit = [rebindAt [buffersKey, name] content] }; - - apply = state \ - content = name & " = " & (join "\n" state.lines) & ";"; - result = eval! content; - _ = debug! "apply" [content, result]; - result - | Defined _ \ { state = state, emit = [] } - | Err msg \ ( - _ = debug! "error applying" []; - { state = state, emit = [] } - ) - | _ \ { state = state, emit = [] }; - insertChar = char state \ newLines = updateAt state.cursorRow (line \ insertCharAt line state.cursorCol char @@ -65,36 +23,6 @@ textEditor = name \ emit = [] }; - # 'o' key - openLine = state \ - stateSnapshot = { lines = state.lines, cursorRow = state.cursorRow, cursorCol = state.cursorCol }; - newUndoStack = [stateSnapshot, ...state.undoStack]; - cursorRow = state.cursorRow + 1; - newLines = insertAt cursorRow "" state.lines; - { - state = state.{ - lines = newLines, - cursorRow = cursorRow, - cursorCol = 0, - undoStack = newUndoStack, - mode = Insert - }, - emit = [] - }; - - deleteLine = state \ - stateSnapshot = { lines = state.lines, cursorRow = state.cursorRow, cursorCol = state.cursorCol }; - newUndoStack = [stateSnapshot, ...state.undoStack]; - newLines = [...(take (state.cursorRow) state.lines), ...(drop (state.cursorRow + 1) state.lines)]; - { - state = clampCursor state.{ - lines = newLines, - undoStack = newUndoStack, - pending = None - }, - emit = [] - }; - escape = state \ { state = state.{ mode = Normal }, emit = [] }; undo = state \ state.undoStack @@ -136,155 +64,139 @@ textEditor = name \ newLines = updateAt state.cursorRow (_ \ before) state.lines; newLines2 = insertAt (state.cursorRow + 1) after newLines; + _ = debug! "enter" { before = before, after = after, newLines2 = newLines2, newRow = + state.cursorRow + 1 }; + { state = state.{ lines = newLines2, cursorCol = 0, cursorRow = state.cursorRow + 1 }, emit = [] }; - upArrow = state \ + clampCursor = state \ + line = nth state.cursorRow state.lines ~ unwrapOr ""; + + newRow = max 0 state.cursorRow; + newRow2 = min (len state.lines - 1) newRow; + + maxCol = state.mode + | Insert \ len line + | Normal \ max 0 (len line - 1); + newCol2 = min maxCol (max 0 state.cursorCol); + + state.{ cursorRow = newRow2, cursorCol = newCol2 }; + + upArrow = state \ ( newState = clampCursor state.{ cursorRow = state.cursorRow - 1 }; - { state = newState, emit = [] }; + { state = newState, emit = [] }); - downArrow = state \ + downArrow = state \ ( newState = clampCursor state.{ cursorRow = state.cursorRow + 1 }; - { state = newState, emit = [] }; + { state = newState, emit = [] }); - leftArrow = state \ + leftArrow = state \ ( newState = clampCursor state.{ cursorCol = state.cursorCol - 1 }; - { state = newState, emit = [] }; + { state = newState, emit = [] }); - rightArrow = state \ + rightArrow = state \ ( newState = clampCursor state.{ cursorCol = state.cursorCol + 1 }; - { state = newState, emit = [] }; + { state = newState, emit = [] }); { - view = ctx \ + view = ctx \ ui.stateful { + focusable = True, + autoFocus = True, - autoScroll = state \ - cursorY = state.cursorRow * lineH; - newScrollY = (cursorY < state.scrollY - | True \ cursorY - | False \ (cursorY + lineH > state.scrollY + ctx.h - | True \ cursorY + lineH - ctx.h - | False \ state.scrollY)); + key = "textEditor-" & name, - cursorX = state.cursorCol * charW * scale + charGap * state.cursorCol; - newScrollX = (cursorX < state.scrollX - | True \ cursorX - | False \ (cursorX + charW * scale > state.scrollX + ctx.w - | True \ cursorX + charW * scale - ctx.w - | False \ state.scrollX)); - state.{ scrollY = newScrollY, scrollX = newScrollX }; + init = { + lines = lines, + cursorRow = 0, + cursorCol = 0, + scrollX = 0, + scrollY = 0, + undoStack = [], + redoStack = [], + mode = Normal # Normal | Insert | Visual + }, - withScroll = result \ - { state = autoScroll result.state, emit = result.emit }; + update = state event \ event + | Key { key = "ArrowDown" } \ downArrow state + | Key { key = "j" } \ (state.mode + | Insert \ insertChar "j" state + | Normal \ downArrow state) - scrollHalfUp = state ctx \ ( - diff = floor ((ctx.h / 2) / lineH); - newRow = state.cursorRow - diff; - withScroll { state = clampCursor state.{ cursorRow = newRow }, emit = [] }); + | Key { key = "ArrowUp" } \ upArrow state + | Key { key = "k" } \ (state.mode + | Insert \ insertChar "k" state + | Normal \ upArrow state) - scrollHalfDown = state ctx \ ( - diff = floor ((ctx.h / 2) / lineH); - newRow = state.cursorRow + diff; - withScroll { state = clampCursor state.{ cursorRow = newRow }, emit = [] }); + | Key { key = "ArrowLeft" } \ leftArrow state + | Key { key = "h" } \ (state.mode + | Insert \ insertChar "h" state + | Normal \ leftArrow state) - ui.stateful { - focusable = True, - autoFocus = True, + | Key { key = "ArrowRight" } \ rightArrow state + | Key { key = "l" } \ (state.mode + | Insert \ insertChar "l" state + | Normal \ rightArrow state) - key = "textEditor-" & name, + | Key { key = "i" } \ (state.mode + | Insert \ insertChar "i" state + | Normal \ insertMode state) - init = { - lines = lines, - cursorRow = 0, - cursorCol = 0, - scrollX = 0, - scrollY = 0, - undoStack = [], - redoStack = [], - mode = Normal, # Normal | Insert | Visual - pending = None # Some "d" | Some "g" | etc. - }, + | Key { key = "u" } \ (state.mode + | Insert \ insertChar "u" state + | Normal \ undo state) - update = state event \ state.mode - | Insert \ (event - | Key { key = "Escape" } \ escape state - | Key { key = "Control" } \ escape state - | Key { key = "Backspace" } \ backspace state - | Key { key = "Enter" } \ enter state - | Key { key = k } \ insertChar k state) - | Normal \ (event - | Scrolled delta \ ( - maxLineLen = fold (acc line \ max acc (len line)) 0 state.lines; - totalW = maxLineLen * charW * scale + maxLineLen * charGap; - totalH = len state.lines * lineH; - newX = max 0 (min (totalW - ctx.w) (state.scrollX + delta.deltaX)); - newY = max 0 (min (totalH - ctx.h) (state.scrollY + delta.deltaY)); - { state = state.{ scrollY = newY, scrollX = newX }, emit = [] }) + | Key { key = "r", ctrl = True } \ (state.mode + | Insert \ insertChar "R" state + | Normal \ redo state) - | Key { key = "ArrowDown" } \ withScroll (downArrow state) - | Key { key = "j" } \ withScroll (downArrow state) - | Key { key = "ArrowUp" } \ withScroll (upArrow state) - | Key { key = "k" } \ withScroll(upArrow state) - | Key { key = "ArrowLeft" } \ withScroll (leftArrow state) - | Key { key = "h" } \ withScroll(leftArrow state) - | Key { key = "ArrowRight" } \ withScroll (rightArrow state) - | Key { key = "l" } \ withScroll(rightArrow state) - | Key { key = "i" } \ insertMode state - | Key { key = "o" } \ openLine state - | Key { key = "d", ctrl = True } \ scrollHalfDown state ctx - | Key { key = "u", ctrl = True } \ scrollHalfUp state ctx - | Key { key = "u" } \ undo state - | Key { key = "d" } \ (state.pending - | Some "d" \ deleteLine state - | _ \ { state = state.{ pending = Some "d" }, emit = [] }) - | Key { key = "r", ctrl = True } \ redo state - | Key { key = "W", ctrl = True, shift = True } \ write state - | Key { key = "A", ctrl = True, shift = True } \ apply state - # any other key or event - | _ \ { state = state, emit = [] } - ), + | Key { key = "Escape" } \ escape state - view = state emit \ - cursorX = state.cursorCol * charW * scale + charGap * state.cursorCol; - cursorY = state.cursorRow * lineH; + | Key { key = "Backspace" } \ (state.mode + | Insert \ backspace state + | _ \ { state = state, emit = [] }) - cursor = ui.positioned { - x = cursorX, - y = cursorY, - child = ui.rect { w = charW * scale, h = charH * scale, color = "rgba(255,255,255,0.5)" } - }; + | Key { key = "Enter" } \ (state.mode + | Insert \ enter state + | _ \ { state = state, emit = [] }) - maxLineLen = fold (acc line \ max acc (len line)) 0 state.lines; - totalWidth = maxLineLen * charW * scale + maxLineLen * charGap; - totalHeight = len state.lines * lineH; + # any other key + | Key { key = key, printable = True } \ (state.mode + | Insert \ insertChar key state + | _ \ { state = state, emit = [] }) - # buffer = map (l \ text l) state.lines; - # only render visible lines - startLine = max 0 (floor (state.scrollY / lineH)); - visibleCount = floor (ctx.h / lineH) + 2; - endLine = min (len state.lines) (startLine + visibleCount); - visibleLines = slice state.lines startLine endLine; - buffer = mapWithIndex (l i \ - ui.positioned { - x = 0, - y = (startLine + i) * lineH, - child = text l - } - ) visibleLines; + | _ \ { state = state, emit = [] }, - scrollable { - w = ctx.w, - h = ctx.h, - totalWidth = totalWidth, - totalHeight = totalHeight, - scrollX = state.scrollX, - scrollY = state.scrollY, - onScroll = delta \ emit (Scrolled delta), - child = ui.stack { - children = [ - ...buffer, - cursor - ] - } + view = state emit \ + buffer = map (l \ text l) state.lines; + + scale = 2; + charH = 12; + charW = 5; + lineGap = 1; + charGap = 2; + + cursor = ui.positioned { + x = state.cursorCol * charW * scale + charGap * state.cursorCol, + y = state.cursorRow * charH * scale + lineGap * state.cursorRow, + child = ui.rect { w = charW * scale, h = charH * scale, color = "rgba(255,255,255,0.5)" } + }; + + scrollable { + w = ctx.w, + h = ctx.h, + totalWidth = 1000, + totalHeight = 1000, + scrollX = state.scrollX, + scrollY = state.scrollY, + child = ui.stack { + children = [ + ui.column { + gap = 1, + children = buffer + }, + cursor + ] } - } + } + } }; diff --git a/src/cg/06-tree.cg b/src/cg/06-tree.cg index f563800..7e3121b 100644 --- a/src/cg/06-tree.cg +++ b/src/cg/06-tree.cg @@ -65,7 +65,7 @@ treeNode = config \ valueLabel = value \ value | NumberValue n \ Some (show n) - | StringValue n \ Some ("\"" & r & "\"") + | StringValue n \ Some ("\"" & n & "\"") | ConstructorValue { tag = tag } \ Some tag | FunctionValue _ \ Some "" | _ \ None; @@ -105,7 +105,7 @@ treeNode = config \ h = 30, onSubmit = onSubmit } - | False \ simple config.path (config.prefix & "\"" & (slice n 0 30) & "..\"") "#f6a" (Some (_ \ config.onEditLeaf config.path))) + | False \ simple config.path (config.prefix & "\"" & n & "\"") "#f6a" (Some (_ \ config.onEditLeaf config.path))) | ConstructorValue { tag = tag } \ (isEditing | True \ textInput { diff --git a/src/runtime-compiled.ts b/src/runtime-compiled.ts index 7426bb4..a06a6bf 100644 --- a/src/runtime-compiled.ts +++ b/src/runtime-compiled.ts @@ -1,6 +1,4 @@ import { render, hitTest, scrollHitTest } from './ui'; -import { syncToAst, saveDefinitions } from './runtime-js'; -import { definitions } from './compiler'; type UIValue = any; @@ -219,28 +217,6 @@ export function runAppCompiled(canvas: HTMLCanvasElement, store: any) { return; } - if (event._tag === 'DeleteAt') { - const path = event._0; - const name = path[0]; - if (path.length === 1) { - delete store[name]; - definitions.delete(name); - saveDefinitions(); - } else { - let obj = store[name]; - for (let i = 1; i < path.length - 1; i++) { - if (obj === undefined || obj === null) return; - obj = obj[path[i]]; - } - if (obj !== undefined && obj !== null) { - delete obj[path[path.length - 1]]; - } - } - syncToAst(name); - - return; - } - if (event._tag === 'Focus') { setFocus(event._0); return; diff --git a/src/runtime-js.ts b/src/runtime-js.ts index db04c8a..2734775 100644 --- a/src/runtime-js.ts +++ b/src/runtime-js.ts @@ -80,7 +80,6 @@ export const _rt = { return String(value); }, chars: (s: string) => s.split(''), - isWordChar: (s: string) => ({ _tag: /^\w$/.test(s) ? 'True' : 'False' }), // join: (delim: string) => (xs: string[]) => xs.join(delim), split: (delim: string) => (xs: string) => xs.split(delim), slice: (s: string | any[]) => (start: number) => (end: number) => s.slice(start, end), @@ -161,9 +160,6 @@ export const _rt = { return { _tag: 'Rebind', _0: name, _1: rest, _2: value }; }, - deleteAt: (path: any[]) => { - return { _tag: 'DeleteAt', _0: path }; - }, "undefine!": (name: string) => { delete store[name]; definitions.delete(name);