summary refs log tree commit diff stats
path: root/compiler/hlo.nim
blob: af54cabbbdbc5f755f3e9b636ed8c974b763ca97 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#
#
#           The Nim Compiler
#        (c) Copyright 2013 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# This include implements the high level optimization pass.

proc hlo(c: PContext, n: PNode): PNode

proc evalPattern(c: PContext, n, orig: PNode): PNode =
  internalAssert c.config, n.kind == nkCall and n[0].kind == nkSym
  # we need to ensure that the resulting AST is semchecked. However, it's
  # awful to semcheck before macro invocation, so we don't and treat
  # templates and macros as immediate in this context.
  var rule: string
  if c.config.hasHint(hintPattern):
    rule = renderTree(n, {renderNoComments})
  let s = n[0].sym
  case s.kind
  of skMacro:
    result = semMacroExpr(c, n, orig, s)
  of skTemplate:
    result = semTemplateExpr(c, n, s, {efFromHlo})
  else:
    result = semDirectOp(c, n, {})
  if c.config.hasHint(hintPattern):
    message(c.config, orig.info, hintPattern, rule & " --> '" &
      renderTree(result, {renderNoComments}) & "'")

proc applyPatterns(c: PContext, n: PNode): PNode =
  result = n
  # we apply the last pattern first, so that pattern overriding is possible;
  # however the resulting AST would better not trigger the old rule then
  # anymore ;-)
  for i in countdown(c.patterns.len-1, 0):
    let pattern = c.patterns[i]
    if not isNil(pattern):
      let x = applyRule(c, pattern, result)
      if not isNil(x):
        assert x.kind in {nkStmtList, nkCall}
        # better be safe than sorry, so check evalTemplateCounter too:
        inc(c.config.evalTemplateCounter)
        if c.config.evalTemplateCounter > evalTemplateLimit:
          globalError(c.config, n.info, "template instantiation too nested")
        # deactivate this pattern:
        c.patterns[i] = nil
        if x.kind == nkStmtList:
          assert x.len == 3
          x[1] = evalPattern(c, x[1], result)
          result = flattenStmts(x)
        else:
          result = evalPattern(c, x, result)
        dec(c.config.evalTemplateCounter)
        # activate this pattern again:
        c.patterns[i] = pattern

proc hlo(c: PContext, n: PNode): PNode =
  inc(c.hloLoopDetector)
  # simply stop and do not perform any further transformations:
  if c.hloLoopDetector > 300: return n
  case n.kind
  of nkMacroDef, nkTemplateDef, procDefs:
    # already processed (special cases in semstmts.nim)
    result = n
  else:
    if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and
        n[0].kind == nkSym and
        {sfGlobal, sfPure} * n[0].sym.flags == {sfGlobal, sfPure}:
      # do not optimize 'var g {.global} = re(...)' again!
      return n
    result = applyPatterns(c, n)
    if result == n:
      # no optimization applied, try subtrees:
      for i in 0..<result.safeLen:
        let a = result[i]
        let h = hlo(c, a)
        if h != a: result[i] = h
    else:
      # perform type checking, so that the replacement still fits:
      if isEmptyType(n.typ) and isEmptyType(result.typ):
        discard
      else:
        result = fitNode(c, n.typ, result, n.info)
      # optimization has been applied so check again:
      result = commonOptimizations(c.graph, c.idgen, c.module, result)
      result = hlo(c, result)
      result = commonOptimizations(c.graph, c.idgen, c.module, result)

proc hloBody(c: PContext, n: PNode): PNode =
  # fast exit:
  if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
  c.hloLoopDetector = 0
  result = hlo(c, n)

proc hloStmt(c: PContext, n: PNode): PNode =
  # fast exit:
  if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
  c.hloLoopDetector = 0
  result = hlo(c, n)
>dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); } static Bool isprotodel(Client *c) { int i, n; Atom *protocols; Bool ret = False; if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { for(i = 0; !ret && i < n; i++) if(protocols[i] == wmatom[WMDelete]) ret = True; XFree(protocols); } return ret; } static void setclientstate(Client *c, long state) { long data[] = {state, None}; XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, PropModeReplace, (unsigned char *)data, 2); } static int xerrordummy(Display *dsply, XErrorEvent *ee) { return 0; } /* extern */ void attach(Client *c) { if(clients) clients->prev = c; c->next = clients; clients = c; } void configure(Client *c) { XConfigureEvent ce; ce.type = ConfigureNotify; ce.display = dpy; ce.event = c->win; ce.window = c->win; ce.x = c->x; ce.y = c->y; ce.width = c->w; ce.height = c->h; ce.border_width = c->border; ce.above = None; ce.override_redirect = False; XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); } void detach(Client *c) { if(c->prev) c->prev->next = c->next; if(c->next) c->next->prev = c->prev; if(c == clients) clients = c->next; c->next = c->prev = NULL; } void focus(Client *c) { if(c && !isvisible(c)) return; if(sel && sel != c) { grabbuttons(sel, False); XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); } if(c) { detachstack(c); attachstack(c); grabbuttons(c, True); } sel = c; drawstatus(); if(!selscreen) return; if(c) { XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); } else XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); } void killclient(const char *arg) { XEvent ev; if(!sel) return; if(isprotodel(sel)) { ev.type = ClientMessage; ev.xclient.window = sel->win; ev.xclient.message_type = wmatom[WMProtocols]; ev.xclient.format = 32; ev.xclient.data.l[0] = wmatom[WMDelete]; ev.xclient.data.l[1] = CurrentTime; XSendEvent(dpy, sel->win, False, NoEventMask, &ev); } else XKillClient(dpy, sel->win); } void manage(Window w, XWindowAttributes *wa) { Client *c, *t = NULL; Window trans; Status rettrans; XWindowChanges wc; c = emallocz(sizeof(Client)); c->tags = emallocz(ntags * sizeof(Bool)); c->win = w; c->x = wa->x; c->y = wa->y; c->w = wa->width; c->h = wa->height; c->oldborder = wa->border_width; if(c->w == sw && c->h == sh) { c->x = sx; c->y = sy; c->border = wa->border_width; } else { if(c->x + c->w + 2 * c->border > wax + waw) c->x = wax + waw - c->w - 2 * c->border; if(c->y + c->h + 2 * c->border > way + wah) c->y = way + wah - c->h - 2 * c->border; if(c->x < wax) c->x = wax; if(c->y < way) c->y = way; c->border = BORDERPX; } wc.border_width = c->border; XConfigureWindow(dpy, w, CWBorderWidth, &wc); XSetWindowBorder(dpy, w, dc.norm[ColBorder]); configure(c); /* propagates border_width, if size doesn't change */ updatesizehints(c); XSelectInput(dpy, w, StructureNotifyMask | PropertyChangeMask | EnterWindowMask); grabbuttons(c, False); updatetitle(c); if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success)) for(t = clients; t && t->win != trans; t = t->next); settags(c, t); if(!c->isfloating) c->isfloating = (rettrans == Success) || c->isfixed; attach(c); attachstack(c); c->isbanned = True; XMoveWindow(dpy, w, c->x + 2 * sw, c->y); XMapWindow(dpy, w); setclientstate(c, NormalState); if(isvisible(c)) focus(c); lt->arrange(); } void resize(Client *c, int x, int y, int w, int h, Bool sizehints) { float dx, dy, max, min, ratio; XWindowChanges wc; if(w <= 0 || h <= 0) return; if(sizehints) { if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0) { dx = (float)(w - c->basew); dy = (float)(h - c->baseh); min = (float)(c->minax) / (float)(c->minay); max = (float)(c->maxax) / (float)(c->maxay); ratio = dx / dy; if(max > 0 && min > 0 && ratio > 0) { if(ratio < min) { dy = (dx * min + dy) / (min * min + 1); dx = dy * min; w = (int)dx + c->basew; h = (int)dy + c->baseh; } else if(ratio > max) { dy = (dx * min + dy) / (max * max + 1); dx = dy * min; w = (int)dx + c->basew; h = (int)dy + c->baseh; } } } if(c->minw && w < c->minw) w = c->minw; if(c->minh && h < c->minh) h = c->minh; if(c->maxw && w > c->maxw) w = c->maxw; if(c->maxh && h > c->maxh) h = c->maxh; if(c->incw) w -= (w - c->basew) % c->incw; if(c->inch) h -= (h - c->baseh) % c->inch; } if(w <= 0 || h <= 0) return; /* offscreen appearance fixes */ if(x > sw) x = sw - w - 2 * c->border; if(y > sh) y = sh - h - 2 * c->border; if(x + w + 2 * c->border < sx) x = sx; if(y + h + 2 * c->border < sy) y = sy; if(c->x != x || c->y != y || c->w != w || c->h != h) { c->x = wc.x = x; c->y = wc.y = y; c->w = wc.width = w; c->h = wc.height = h; wc.border_width = c->border; XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); configure(c); XSync(dpy, False); } } void togglefloating(const char *arg) { if(!sel || lt->arrange == floating) return; sel->isfloating = !sel->isfloating; lt->arrange(); } void updatesizehints(Client *c) { long msize; XSizeHints size; if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) size.flags = PSize; c->flags = size.flags; if(c->flags & PBaseSize) { c->basew = size.base_width; c->baseh = size.base_height; } else if(c->flags & PMinSize) { c->basew = size.min_width; c->baseh = size.min_height; } else c->basew = c->baseh = 0; if(c->flags & PResizeInc) { c->incw = size.width_inc; c->inch = size.height_inc; } else c->incw = c->inch = 0; if(c->flags & PMaxSize) { c->maxw = size.max_width; c->maxh = size.max_height; } else c->maxw = c->maxh = 0; if(c->flags & PMinSize) { c->minw = size.min_width; c->minh = size.min_height; } else if(c->flags & PBaseSize) { c->minw = size.base_width; c->minh = size.base_height; } else c->minw = c->minh = 0; if(c->flags & PAspect) { c->minax = size.min_aspect.x; c->maxax = size.max_aspect.x; c->minay = size.min_aspect.y; c->maxay = size.max_aspect.y; } else c->minax = c->maxax = c->minay = c->maxay = 0; c->isfixed = (c->maxw && c->minw && c->maxh && c->minh && c->maxw == c->minw && c->maxh == c->minh); } void updatetitle(Client *c) { char **list = NULL; int n; XTextProperty name; name.nitems = 0; c->name[0] = 0; XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]); if(!name.nitems) XGetWMName(dpy, c->win, &name); if(!name.nitems) return; if(name.encoding == XA_STRING) strncpy(c->name, (char *)name.value, sizeof c->name); else { if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { strncpy(c->name, *list, sizeof c->name); XFreeStringList(list); } } XFree(name.value); } void unmanage(Client *c) { Client *nc; XWindowChanges wc; wc.border_width = c->oldborder; /* The server grab construct avoids race conditions. */ XGrabServer(dpy); XSetErrorHandler(xerrordummy); XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ detach(c); detachstack(c); if(sel == c) { for(nc = stack; nc && !isvisible(nc); nc = nc->snext); focus(nc); } XUngrabButton(dpy, AnyButton, AnyModifier, c->win); setclientstate(c, WithdrawnState); free(c->tags); free(c); XSync(dpy, False); XSetErrorHandler(xerror); XUngrabServer(dpy); lt->arrange(); }