about summary refs log tree commit diff stats
path: root/linux/ex11.subx
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2021-03-08 23:49:07 -0800
committerKartik K. Agaram <vc@akkartik.com>2021-03-08 23:50:35 -0800
commitcec5ef31b3e383b7bdffe049a8c502a563f6b491 (patch)
tree9f6b410cc16991a709dc59258ae29dacd2feb98b /linux/ex11.subx
parent6508ab51ccd6a41d6d1da3502359e80611d8bda3 (diff)
downloadmu-cec5ef31b3e383b7bdffe049a8c502a563f6b491.tar.gz
update vocabulary documentation
Top-level and linux/ now have separate vocabulary.md files.
Diffstat (limited to 'linux/ex11.subx')
0 files changed, 0 insertions, 0 deletions
> 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
import streams
import tables

import css/mediaquery
import css/cssparser
import css/selectorparser
import html/tags

type
  CSSRuleBase* = ref object of RootObj

  CSSRuleDef* = ref object of CSSRuleBase
    sels*: SelectorList
    decls*: seq[CSSDeclaration]

  CSSConditionalDef* = ref object of CSSRuleBase
    children*: CSSStylesheet

  CSSMediaQueryDef* = ref object of CSSConditionalDef
    query*: MediaQueryList

  CSSStylesheet* = ref object
    mq_list*: seq[CSSMediaQueryDef]
    tag_table*: array[TagType, seq[CSSRuleDef]]
    id_table*: TableRef[string, seq[CSSRuleDef]]
    class_table*: TableRef[string, seq[CSSRuleDef]]
    general_list*: seq[CSSRuleDef]
    len*: int

type SelectorHashes = object
  tag: TagType
  id: string
  class: string

func newStylesheet*(cap: int): CSSStylesheet =
  new(result)
  let bucketsize = cap div 2
  result.id_table = newTable[string, seq[CSSRuleDef]](bucketsize)
  result.class_table = newTable[string, seq[CSSRuleDef]](bucketsize)
  result.general_list = newSeqOfCap[CSSRuleDef](bucketsize)

proc getSelectorIds(hashes: var SelectorHashes, sel: Selector): bool

proc getSelectorIds(hashes: var SelectorHashes, sels: CompoundSelector) =
  for sel in sels:
    if hashes.getSelectorIds(sel):
      break

# For now, we match elements against the *last* selector.
#TODO this is inefficient, so we should eventually get rid of this
# function
proc getSelectorIds(hashes: var SelectorHashes, cxsel: ComplexSelector) =
  hashes.getSelectorIds(cxsel[^1])

proc getSelectorIds(hashes: var SelectorHashes, sel: Selector): bool =
  case sel.t
  of TYPE_SELECTOR:
    hashes.tag = sel.tag
    return true
  of CLASS_SELECTOR:
    hashes.class = sel.class
    return true
  of ID_SELECTOR:
    hashes.id = sel.id
    return true
  of ATTR_SELECTOR, PSELEM_SELECTOR, UNIVERSAL_SELECTOR:
    return false
  of PSEUDO_SELECTOR:
    if sel.pseudo.t in {PSEUDO_IS, PSEUDO_WHERE}:
      # Basically just hash whatever the selectors have in common:
      #1. get the hashable values of selector 1
      #2. for every other selector x:
      #3.   get hashable values of selector x
      #4.   store hashable values of selector x that aren't stored yet
      #5.   for every hashable value of selector 1 that doesn't match selector x
      #6.     cancel hashable value
      var cancel_tag = false
      var cancel_id = false
      var cancel_class = false
      var i = 0
      if i < sel.pseudo.fsels.len:
        hashes.getSelectorIds(sel.pseudo.fsels[i])
        inc i

      while i < sel.pseudo.fsels.len:
        var nhashes: SelectorHashes
        nhashes.getSelectorIds(sel.pseudo.fsels[i])
        if hashes.tag == TAG_UNKNOWN:
          hashes.tag = nhashes.tag
        elif not cancel_tag and nhashes.tag != TAG_UNKNOWN and nhashes.tag != hashes.tag:
          cancel_tag = true

        if hashes.id == "":
          hashes.id = nhashes.id
        elif not cancel_id and nhashes.id != "" and nhashes.id != hashes.id:
          cancel_id = true

        if hashes.class == "":
          hashes.class = nhashes.class
        elif not cancel_class and nhashes.class != "" and nhashes.class != hashes.class:
          cancel_class = true

        inc i

      if cancel_tag:
        hashes.tag = TAG_UNKNOWN
      if cancel_id:
        hashes.id = ""
      if cancel_class:
        hashes.class = ""

      if hashes.tag != TAG_UNKNOWN or hashes.id != "" or hashes.class != "":
        return true

iterator gen_rules*(sheet: CSSStylesheet, tag: TagType, id: string, classes: seq[string]): CSSRuleDef =
  for rule in sheet.tag_table[tag]:
    yield rule
  if id != "":
    if sheet.id_table.hasKey(id):
      for rule in sheet.id_table[id]:
        yield rule
  if classes.len > 0:
    for class in classes:
      if sheet.class_table.hasKey(class):
        for rule in sheet.class_table[class]:
          yield rule
  for rule in sheet.general_list:
    yield rule

proc add(sheet: var CSSStylesheet, rule: CSSRuleDef) =
  var hashes: SelectorHashes
  for cxsel in rule.sels:
    hashes.getSelectorIds(cxsel)
    if hashes.tag != TAG_UNKNOWN:
      sheet.tag_table[hashes.tag].add(rule)
    elif hashes.id != "":
      if hashes.id notin sheet.id_table:
        sheet.id_table[hashes.id] = newSeq[CSSRuleDef]()
      sheet.id_table[hashes.id].add(rule)
    elif hashes.class != "":
      if hashes.class notin sheet.class_table:
        sheet.class_table[hashes.class] = newSeq[CSSRuleDef]()
      sheet.class_table[hashes.class].add(rule)
    else:
      sheet.general_list.add(rule)

proc add*(sheet: var CSSStylesheet, rule: CSSRuleBase) {.inline.} =
  if rule of CSSRuleDef:
    sheet.add(CSSRuleDef(rule))
  else:
    sheet.mq_list.add(CSSMediaQueryDef(rule))
  inc sheet.len

proc add*(sheet: var CSSStylesheet, sheet2: CSSStylesheet) {.inline.} =
  sheet.general_list.add(sheet2.general_list)
  for tag in TagType:
    sheet.tag_table[tag].add(sheet2.tag_table[tag])
  for key, value in sheet2.id_table.pairs:
    if key notin sheet.id_table:
      sheet.id_table[key] = newSeq[CSSRuleDef]()
    sheet.id_table[key].add(value)
  for key, value in sheet2.class_table.pairs:
    if key notin sheet.class_table:
      sheet.class_table[key] = newSeq[CSSRuleDef]()
    sheet.class_table[key].add(value)
  sheet.len += sheet2.len

proc getDeclarations(rule: CSSQualifiedRule): seq[CSSDeclaration] {.inline.} =
  rule.oblock.value.parseListOfDeclarations2()

proc addRule(stylesheet: var CSSStylesheet, rule: CSSQualifiedRule) =
  let sels = parseSelectors(rule.prelude)
  if sels.len > 0:
    let r = CSSRuleDef(sels: sels, decls: rule.getDeclarations())
    stylesheet.add(r)

proc addAtRule(stylesheet: var CSSStylesheet, atrule: CSSAtRule) =
  case atrule.name
  of "media":
    let query = parseMediaQueryList(atrule.prelude)
    let rules = atrule.oblock.value.parseListOfRules()
    if rules.len > 0:
      var media = CSSMediaQueryDef()
      media.children = newStylesheet(rules.len)
      media.query = query
      for rule in rules:
        if rule of CSSAtRule:
          media.children.addAtRule(CSSAtRule(rule))
        else:
          media.children.addRule(CSSQualifiedRule(rule))
      stylesheet.add(media)
  else: discard #TODO

proc parseStylesheet*(s: Stream): CSSStylesheet =
  let css = parseCSS(s)
  result = newStylesheet(css.value.len)
  for v in css.value:
    if v of CSSAtRule: result.addAtRule(CSSAtRule(v))
    else: result.addRule(CSSQualifiedRule(v))
  s.close()

proc parseStylesheet*(s: string): CSSStylesheet =
  return newStringStream(s).parseStylesheet()