diff options
author | bptato <nincsnevem662@gmail.com> | 2023-06-25 13:23:27 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2023-06-25 13:23:27 +0200 |
commit | 468d0823bf5c16e0a568858704c073a4896fcb8d (patch) | |
tree | 7f0460c0678613bfd90e6e0c3141c81e009040b2 /src/css | |
parent | fd837c155acca689839c5ce184fd5059caa83770 (diff) | |
download | chawan-468d0823bf5c16e0a568858704c073a4896fcb8d.tar.gz |
css/values: use Result instead of exceptions
Diffstat (limited to 'src/css')
-rw-r--r-- | src/css/mediaquery.nim | 7 | ||||
-rw-r--r-- | src/css/values.nim | 643 |
2 files changed, 342 insertions, 308 deletions
diff --git a/src/css/mediaquery.nim b/src/css/mediaquery.nim index 8d75be2a..3a594e6c 100644 --- a/src/css/mediaquery.nim +++ b/src/css/mediaquery.nim @@ -3,6 +3,7 @@ import tables import css/cssparser import css/values +import utils/opt import utils/twtstr type @@ -199,10 +200,10 @@ template expect_int_range(range: var Slice[int], ismin, ismax: bool) = template expect_length(length: var CSSLength) = let cval = parser.consume() - try: - length = cssLength(cval) - except CSSValueError: + let r = cssLength(cval) + if r.isErr: return nil + length = r.get template expect_length_range(range: var Slice[CSSLength], lengthaeq, lengthbeq: var bool, ismin, ismax: bool) = diff --git a/src/css/values.nim b/src/css/values.nim index dd479509..f92119fe 100644 --- a/src/css/values.nim +++ b/src/css/values.nim @@ -9,6 +9,7 @@ import css/selectorparser import img/bitmap import io/window import types/color +import utils/opt import utils/twtstr export selectorparser.PseudoElem @@ -216,8 +217,6 @@ type importantProperties: array[CSSOrigin, CSSComputedValueBuilders] preshints*: CSSComputedValues - CSSValueError* = object of ValueError - const ShorthandNames = { "all": SHORTHAND_ALL, "margin": SHORTHAND_MARGIN, @@ -467,11 +466,10 @@ const Units = { "vmax": UNIT_VMAX, }.toTable() -func cssLength(val: float64, unit: string): CSSLength = +func cssLength(val: float64, unit: string): Result[CSSLength, string] = if unit in Units: - CSSLength(num: val, unit: Units[unit]) - else: - raise newException(CSSValueError, "Invalid unit") + return ok(CSSLength(num: val, unit: Units[unit])) + return err("Invalid unit") const CSSLengthAuto* = CSSLength(auto: true) @@ -504,18 +502,18 @@ func skipWhitespace(vals: seq[CSSComponentValue], i: var int) = break inc i -func cssColor*(val: CSSComponentValue): RGBAColor = +func cssColor*(val: CSSComponentValue): Result[RGBAColor, string] = if val of CSSToken: let tok = CSSToken(val) case tok.tokenType of CSS_HASH_TOKEN: let c = parseHexColor(tok.value) if c.isSome: - return c.get + return ok(c.get) of CSS_IDENT_TOKEN: let s = tok.value if s in Colors: - return Colors[s] + return ok(Colors[s]) else: discard elif val of CSSFunction: let f = CSSFunction(val) @@ -525,7 +523,7 @@ func cssColor*(val: CSSComponentValue): RGBAColor = #TODO calc, percentages, etc (cssnumber function or something) if not slash and i >= f.value.len or i < f.value.len and f.value[i] != CSS_NUMBER_TOKEN: - raise newException(CSSValueError, "Invalid color") + return err("Invalid color") template next_value(first = false, slash = false) = inc i f.value.skipWhitespace(i) @@ -536,10 +534,10 @@ func cssColor*(val: CSSComponentValue): RGBAColor = f.value.skipWhitespace(i) commaMode = true elif commaMode: - raise newException(CSSValueError, "Invalid color") + return err("Invalid color") elif slash: if f.value[i] != CSS_DELIM_TOKEN or CSSToken(f.value[i]).rvalue != Rune('/'): - raise newException(CSSValueError, "Invalid color") + return err("Invalid color") inc i f.value.skipWhitespace(i) check_err slash @@ -557,21 +555,35 @@ func cssColor*(val: CSSComponentValue): RGBAColor = CSSToken(f.value[i]).nvalue else: 1 - return rgba(int(r), int(g), int(b), int(a * 255)) + return ok(rgba(int(r), int(g), int(b), int(a * 255))) else: discard - raise newException(CSSValueError, "Invalid color") + return err("Invalid color") func isToken(cval: CSSComponentValue): bool {.inline.} = cval of CSSToken +func getToken(cval: CSSComponentValue): CSSToken = (CSSToken)cval + +func cssIdent[T](map: static Table[string, T], cval: CSSComponentValue): + Result[T, string] = + if isToken(cval): + let tok = getToken(cval) + if tok.tokenType == CSS_IDENT_TOKEN: + let val = tok.value + if val in map: + return ok(map[val]) + when T is CSSDisplay: + eprint "invalid", cval + return err("Invalid ident") + func cssLength*(val: CSSComponentValue, has_auto: static bool = true, - allow_negative: static bool = true): CSSLength = + allow_negative: static bool = true): Result[CSSLength, string] = block nofail: if val of CSSToken: let tok = CSSToken(val) case tok.tokenType of CSS_NUMBER_TOKEN: if tok.nvalue == 0: - return CSSLength(num: 0, unit: UNIT_PX) + return ok(CSSLength(num: 0, unit: UNIT_PX)) of CSS_PERCENTAGE_TOKEN: when not allow_negative: if tok.nvalue < 0: @@ -585,24 +597,24 @@ func cssLength*(val: CSSComponentValue, has_auto: static bool = true, of CSS_IDENT_TOKEN: when has_auto: if tok.value == "auto": - return CSSLengthAuto + return ok(CSSLengthAuto) else: discard - raise newException(CSSValueError, "Invalid length") + return err("Invalid length") -func cssAbsoluteLength(val: CSSComponentValue): CSSLength = +func cssAbsoluteLength(val: CSSComponentValue): Result[CSSLength, string] = if val of CSSToken: let tok = CSSToken(val) case tok.tokenType of CSS_NUMBER_TOKEN: if tok.nvalue == 0: - return CSSLength(num: 0, unit: UNIT_PX) + return ok(CSSLength(num: 0, unit: UNIT_PX)) of CSS_DIMENSION_TOKEN: if tok.nvalue >= 0: return cssLength(tok.nvalue, tok.unit) else: discard - raise newException(CSSValueError, "Invalid length") + return err("Invalid length") -func cssWordSpacing(cval: CSSComponentValue): CSSLength = +func cssWordSpacing(cval: CSSComponentValue): Result[CSSLength, string] = if cval of CSSToken: let tok = CSSToken(cval) case tok.tokenType @@ -610,11 +622,9 @@ func cssWordSpacing(cval: CSSComponentValue): CSSLength = return cssLength(tok.nvalue, tok.unit) of CSS_IDENT_TOKEN: if tok.value == "normal": - return CSSLengthAuto + return ok(CSSLengthAuto) else: discard - raise newException(CSSValueError, "Invalid word spacing") - -func getToken(cval: CSSComponentValue): CSSToken = (CSSToken)cval + return err("Invalid word spacing") func cssGlobal*(d: CSSDeclaration): CSSGlobalValueType = if d.value.len > 0 and isToken(d.value[0]): @@ -627,9 +637,9 @@ func cssGlobal*(d: CSSDeclaration): CSSGlobalValueType = of "revert": return VALUE_REVERT return VALUE_NOGLOBAL -func cssQuotes(d: CSSDeclaration): CSSQuotes = +func cssQuotes(d: CSSDeclaration): Result[CSSQuotes, string] = template die = - raise newException(CSSValueError, "Invalid quotes") + return err("Invalid quotes") var res: CSSQuotes var sa = false var pair: tuple[s, e: string] @@ -656,7 +666,7 @@ func cssQuotes(d: CSSDeclaration): CSSQuotes = else: die if sa: die - return res + return ok(res) func cssContent(d: CSSDeclaration): seq[CSSContent] = for cval in d.value: @@ -674,62 +684,62 @@ func cssContent(d: CSSDeclaration): seq[CSSContent] = result.add(CSSContent(t: CONTENT_STRING, s: tok.value)) else: return -func cssDisplay(cval: CSSComponentValue): CSSDisplay = - if isToken(cval): - let tok = getToken(cval) - if tok.tokenType == CSS_IDENT_TOKEN: - case tok.value - of "block": return DISPLAY_BLOCK - of "inline": return DISPLAY_INLINE - of "list-item": return DISPLAY_LIST_ITEM - of "inline-block": return DISPLAY_INLINE_BLOCK - of "table": return DISPLAY_TABLE - of "table-row": return DISPLAY_TABLE_ROW - of "table-cell": return DISPLAY_TABLE_CELL - of "table-column": return DISPLAY_TABLE_COLUMN - of "table-column-group": return DISPLAY_TABLE_COLUMN_GROUP - of "inline-table": return DISPLAY_INLINE_TABLE - of "table-row-group": return DISPLAY_TABLE_ROW_GROUP - of "table-header-group": return DISPLAY_TABLE_HEADER_GROUP - of "table-footer-group": return DISPLAY_TABLE_FOOTER_GROUP - of "table-caption": return DISPLAY_TABLE_CAPTION - of "none": return DISPLAY_NONE - raise newException(CSSValueError, "Invalid display") - -func cssFontStyle(cval: CSSComponentValue): CSSFontStyle = + +func cssDisplay(cval: CSSComponentValue): Result[CSSDisplay, string] = + const DisplayMap = { + "block": DISPLAY_BLOCK, + "inline": DISPLAY_INLINE, + "list-item": DISPLAY_LIST_ITEM, + "inline-block": DISPLAY_INLINE_BLOCK, + "table": DISPLAY_TABLE, + "table-row": DISPLAY_TABLE_ROW, + "table-cell": DISPLAY_TABLE_CELL, + "table-column": DISPLAY_TABLE_COLUMN, + "table-column-group": DISPLAY_TABLE_COLUMN_GROUP, + "inline-table": DISPLAY_INLINE_TABLE, + "table-row-group": DISPLAY_TABLE_ROW_GROUP, + "table-header-group": DISPLAY_TABLE_HEADER_GROUP, + "table-footer-group": DISPLAY_TABLE_FOOTER_GROUP, + "table-caption": DISPLAY_TABLE_CAPTION, + "none": DISPLAY_NONE + }.toTable() + return cssIdent(DisplayMap, cval) + +func cssFontStyle(cval: CSSComponentValue): Result[CSSFontStyle, string] = if isToken(cval): let tok = getToken(cval) if tok.tokenType == CSS_IDENT_TOKEN: case tok.value - of "normal": return FONTSTYLE_NORMAL - of "italic": return FONTSTYLE_ITALIC - of "oblique": return FONTSTYLE_OBLIQUE - raise newException(CSSValueError, "Invalid font style") + of "normal": return ok(FONTSTYLE_NORMAL) + of "italic": return ok(FONTSTYLE_ITALIC) + of "oblique": return ok(FONTSTYLE_OBLIQUE) + return err("Invalid font style") -func cssWhiteSpace(cval: CSSComponentValue): CSSWhitespace = +func cssWhiteSpace(cval: CSSComponentValue): Result[CSSWhitespace, string] = if isToken(cval): let tok = getToken(cval) if tok.tokenType == CSS_IDENT_TOKEN: case tok.value - of "normal": return WHITESPACE_NORMAL - of "nowrap": return WHITESPACE_NOWRAP - of "pre": return WHITESPACE_PRE - of "pre-line": return WHITESPACE_PRE_LINE - of "pre-wrap": return WHITESPACE_PRE_WRAP - raise newException(CSSValueError, "Invalid whitespace") - -func cssFontWeight(cval: CSSComponentValue): int = + of "normal": return ok(WHITESPACE_NORMAL) + of "nowrap": return ok(WHITESPACE_NOWRAP) + of "pre": return ok(WHITESPACE_PRE) + of "pre-line": return ok(WHITESPACE_PRE_LINE) + of "pre-wrap": return ok(WHITESPACE_PRE_WRAP) + return err("Invalid whitespace") + +func cssFontWeight(cval: CSSComponentValue): Result[int, string] = if isToken(cval): let tok = getToken(cval) if tok.tokenType == CSS_IDENT_TOKEN: case tok.value - of "normal": return 400 - of "bold": return 700 - of "lighter": return 400 - of "bolder": return 700 + of "normal": return ok(400) + of "bold": return ok(700) + of "lighter": return ok(400) + of "bolder": return ok(700) elif tok.tokenType == CSS_NUMBER_TOKEN: - return int(tok.nvalue) - raise newException(CSSValueError, "Invalid font weight") + if tok.nvalue in 1f64..1000f64: + return ok(int(tok.nvalue)) + return err("Invalid font weight") func cssTextDecoration(d: CSSDeclaration): set[CSSTextDecoration] = for cval in d.value: @@ -743,53 +753,54 @@ func cssTextDecoration(d: CSSDeclaration): set[CSSTextDecoration] = of "line-through": result.incl(TEXT_DECORATION_LINE_THROUGH) of "blink": result.incl(TEXT_DECORATION_BLINK) -func cssWordBreak(cval: CSSComponentValue): CSSWordBreak = +func cssWordBreak(cval: CSSComponentValue): Result[CSSWordBreak, string] = if isToken(cval): let tok = getToken(cval) if tok.tokenType == CSS_IDENT_TOKEN: case tok.value - of "normal": return WORD_BREAK_NORMAL - of "break-all": return WORD_BREAK_BREAK_ALL - of "keep-all": return WORD_BREAK_KEEP_ALL - raise newException(CSSValueError, "Invalid text decoration") - -func cssListStyleType(cval: CSSComponentValue): CSSListStyleType = - if isToken(cval): - let tok = getToken(cval) - if tok.tokenType == CSS_IDENT_TOKEN: - case tok.value - of "none": return LIST_STYLE_TYPE_NONE - of "disc": return LIST_STYLE_TYPE_DISC - of "circle": return LIST_STYLE_TYPE_CIRCLE - of "square": return LIST_STYLE_TYPE_SQUARE - of "decimal": return LIST_STYLE_TYPE_DECIMAL - of "upper-roman": return LIST_STYLE_TYPE_UPPER_ROMAN - of "lower-roman": return LIST_STYLE_TYPE_LOWER_ROMAN - of "japanese-informal": return LIST_STYLE_TYPE_JAPANESE_INFORMAL - raise newException(CSSValueError, "Invalid list style") - -func cssVerticalAlign(cval: CSSComponentValue): CSSVerticalAlign = + of "normal": return ok(WORD_BREAK_NORMAL) + of "break-all": return ok(WORD_BREAK_BREAK_ALL) + of "keep-all": return ok(WORD_BREAK_KEEP_ALL) + return err("Invalid text decoration") + +func cssListStyleType(cval: CSSComponentValue): + Result[CSSListStyleType, string] = + const ListStyleMap = { + "none": LIST_STYLE_TYPE_NONE, + "disc": LIST_STYLE_TYPE_DISC, + "circle": LIST_STYLE_TYPE_CIRCLE, + "square": LIST_STYLE_TYPE_SQUARE, + "decimal": LIST_STYLE_TYPE_DECIMAL, + "upper-roman": LIST_STYLE_TYPE_UPPER_ROMAN, + "lower-roman": LIST_STYLE_TYPE_LOWER_ROMAN, + "japanese-informal": LIST_STYLE_TYPE_JAPANESE_INFORMAL + }.toTable() + return cssIdent(ListStyleMap, cval) + +func cssVerticalAlign(cval: CSSComponentValue): + Result[CSSVerticalAlign, string] = if isToken(cval): + var res = CSSVerticalAlign() let tok = getToken(cval) if tok.tokenType == CSS_IDENT_TOKEN: case tok.value - of "baseline": result.keyword = VERTICAL_ALIGN_BASELINE - of "sub": result.keyword = VERTICAL_ALIGN_SUB - of "super": result.keyword = VERTICAL_ALIGN_SUPER - of "text-top": result.keyword = VERTICAL_ALIGN_TEXT_BOTTOM - of "middle": result.keyword = VERTICAL_ALIGN_MIDDLE - of "top": result.keyword = VERTICAL_ALIGN_TOP - of "bottom": result.keyword = VERTICAL_ALIGN_BOTTOM + of "baseline": res.keyword = VERTICAL_ALIGN_BASELINE + of "sub": res.keyword = VERTICAL_ALIGN_SUB + of "super": res.keyword = VERTICAL_ALIGN_SUPER + of "text-top": res.keyword = VERTICAL_ALIGN_TEXT_BOTTOM + of "middle": res.keyword = VERTICAL_ALIGN_MIDDLE + of "top": res.keyword = VERTICAL_ALIGN_TOP + of "bottom": res.keyword = VERTICAL_ALIGN_BOTTOM else: - raise newException(CSSValueError, "Invalid vertical align") - return result + return err("Invalid vertical align") + return ok(res) else: - result.keyword = VERTICAL_ALIGN_BASELINE - result.length = cssLength(tok, has_auto = false) - return result - raise newException(CSSValueError, "Invalid vertical align") + res.keyword = VERTICAL_ALIGN_BASELINE + res.length = ?cssLength(tok, has_auto = false) + return ok(res) + return err("Invalid vertical align") -func cssLineHeight(cval: CSSComponentValue): CSSLength = +func cssLineHeight(cval: CSSComponentValue): Result[CSSLength, string] = if cval of CSSToken: let tok = CSSToken(cval) case tok.tokenType @@ -797,76 +808,74 @@ func cssLineHeight(cval: CSSComponentValue): CSSLength = return cssLength(tok.nvalue * 100, "%") of CSS_IDENT_TOKEN: if tok.value == "normal": - return CSSLengthAuto + return ok(CSSLengthAuto) else: return cssLength(tok, has_auto = false) - raise newException(CSSValueError, "Invalid line height") - -func cssTextAlign(cval: CSSComponentValue): CSSTextAlign = + return err("Invalid line height") + +func cssTextAlign(cval: CSSComponentValue): Result[CSSTextAlign, string] = + const TextAlignMap = { + "start": TEXT_ALIGN_START, + "end": TEXT_ALIGN_END, + "left": TEXT_ALIGN_LEFT, + "right": TEXT_ALIGN_RIGHT, + "center": TEXT_ALIGN_CENTER, + "justify": TEXT_ALIGN_JUSTIFY, + "-cha-center": TEXT_ALIGN_CHA_CENTER + }.toTable() + return cssIdent(TextAlignMap, cval) + +func cssListStylePosition(cval: CSSComponentValue): + Result[CSSListStylePosition, string] = if isToken(cval): let tok = getToken(cval) if tok.tokenType == CSS_IDENT_TOKEN: - case tok.value - of "start": return TEXT_ALIGN_START - of "end": return TEXT_ALIGN_END - of "left": return TEXT_ALIGN_LEFT - of "right": return TEXT_ALIGN_RIGHT - of "center": return TEXT_ALIGN_CENTER - of "justify": return TEXT_ALIGN_JUSTIFY - of "-cha-center": return TEXT_ALIGN_CHA_CENTER - raise newException(CSSValueError, "Invalid text align") - -func cssListStylePosition(cval: CSSComponentValue): CSSListStylePosition = + if tok.value == "inside": + return ok(LIST_STYLE_POSITION_INSIDE) + elif tok.value == "outside": + return ok(LIST_STYLE_POSITION_OUTSIDE) + return err("Invalid ident") + +func cssPosition(cval: CSSComponentValue): Result[CSSPosition, string] = + const PositionMap = { + "static": POSITION_STATIC, + "relative": POSITION_RELATIVE, + "absolute": POSITION_ABSOLUTE, + "fixed": POSITION_FIXED, + "sticky": POSITION_STICKY + }.toTable() + return cssIdent(PositionMap, cval) + +func cssCaptionSide(cval: CSSComponentValue): Result[CSSCaptionSide, string] = + const CaptionSideMap = { + "top": CAPTION_SIDE_TOP, + "bottom": CAPTION_SIDE_BOTTOM, + "left": CAPTION_SIDE_LEFT, + "right": CAPTION_SIDE_RIGHT, + "block-start": CAPTION_SIDE_BLOCK_START, + "block-end": CAPTION_SIDE_BLOCK_END, + "inline-start": CAPTION_SIDE_INLINE_START, + "inline-end": CAPTION_SIDE_INLINE_END + }.toTable() + return cssIdent(CaptionSideMap, cval) + +func cssBorderCollapse(cval: CSSComponentValue): + Result[CSSBorderCollapse, string] = if isToken(cval): let tok = getToken(cval) if tok.tokenType == CSS_IDENT_TOKEN: - case tok.value - of "outside": return LIST_STYLE_POSITION_OUTSIDE - of "inside": return LIST_STYLE_POSITION_INSIDE - raise newException(CSSValueError, "Invalid list style position") + if tok.value == "collapse": + return ok(BORDER_COLLAPSE_COLLAPSE) + elif tok.value == "separate": + return ok(BORDER_COLLAPSE_SEPARATE) + return err("Invalid ident") -func cssPosition(cval: CSSComponentValue): CSSPosition = - if isToken(cval): - let tok = getToken(cval) - if tok.tokenType == CSS_IDENT_TOKEN: - case tok.value - of "static": return POSITION_STATIC - of "relative": return POSITION_RELATIVE - of "absolute": return POSITION_ABSOLUTE - of "fixed": return POSITION_FIXED - of "sticky": return POSITION_STICKY - else: raise newException(CSSValueError, "Invalid list style position") - raise newException(CSSValueError, "Invalid list style position") - -func cssCaptionSide(cval: CSSComponentValue): CSSCaptionSide = - if isToken(cval): - let tok = getToken(cval) - if tok.tokenType == CSS_IDENT_TOKEN: - case tok.value - of "top": return CAPTION_SIDE_TOP - of "bottom": return CAPTION_SIDE_BOTTOM - of "left": return CAPTION_SIDE_LEFT - of "right": return CAPTION_SIDE_RIGHT - of "block-start": return CAPTION_SIDE_BLOCK_START - of "block-end": return CAPTION_SIDE_BLOCK_END - of "inline-start": return CAPTION_SIDE_INLINE_START - of "inline-end": return CAPTION_SIDE_INLINE_END - raise newException(CSSValueError, "Invalid caption side") - -func cssBorderCollapse(cval: CSSComponentValue): CSSBorderCollapse = - if isToken(cval): - let tok = getToken(cval) - if tok.tokenType == CSS_IDENT_TOKEN: - case tok.value - of "collapse": return BORDER_COLLAPSE_COLLAPSE - of "separate": return BORDER_COLLAPSE_SEPARATE - raise newException(CSSValueError, "Invalid border collapse") - -func cssCounterReset(d: CSSDeclaration): seq[CSSCounterReset] = +func cssCounterReset(d: CSSDeclaration): Result[seq[CSSCounterReset], string] = template die = - raise newException(CSSValueError, "Invalid counter-reset") + return err("Invalid counter-reset") var r: CSSCounterReset var s = false + var res: seq[CSSCounterReset] for cval in d.value: if isToken(cval): let tok = getToken(cval) @@ -881,22 +890,23 @@ func cssCounterReset(d: CSSDeclaration): seq[CSSCounterReset] = if not s: die r.num = int(tok.nvalue) - result.add(r) + res.add(r) s = false else: die + return ok(res) -func cssMaxMinSize(cval: CSSComponentValue): CSSLength = +func cssMaxMinSize(cval: CSSComponentValue): Result[CSSLength, string] = if isToken(cval): let tok = getToken(cval) case tok.tokenType of CSS_IDENT_TOKEN: if tok.value == "none": - return CSSLengthAuto + return ok(CSSLengthAuto) of CSS_NUMBER_TOKEN, CSS_DIMENSION_TOKEN: return cssLength(tok, allow_negative = false) else: discard - raise newException(CSSValueError, "Invalid min/max-size") + return err("Invalid min/max-size") #TODO should be URL (parsed with baseurl of document...) func cssURL(cval: CSSComponentValue): Option[string] = @@ -919,104 +929,125 @@ func cssURL(cval: CSSComponentValue): Option[string] = break #TODO this should be bg-image, add gradient, etc etc -func cssImage(cval: CSSComponentValue): CSSContent = +func cssImage(cval: CSSComponentValue): Result[CSSContent, string] = if isToken(cval): #TODO bg-image only let tok = getToken(cval) if tok.tokenType == CSS_IDENT_TOKEN and tok.value == "none": - return CSSContent(t: CONTENT_IMAGE, s: "") + return ok(CSSContent(t: CONTENT_IMAGE, s: "")) let url = cssURL(cval) if url.isSome: - return CSSContent(t: CONTENT_IMAGE, s: url.get) - raise newException(CSSValueError, "Invalid image") + return ok(CSSContent(t: CONTENT_IMAGE, s: url.get)) + return err("Invalid image") -func cssInteger(cval: CSSComponentValue, range: Slice[int]): int = +func cssInteger(cval: CSSComponentValue, range: Slice[int]): + Result[int, string] = if isToken(cval): let tok = getToken(cval) if tok.tokenType == CSS_NUMBER_TOKEN: - let i = int(tok.nvalue) - if float64(i) == tok.nvalue and i in range: - return i - raise newException(CSSValueError, "Invalid integer") + if tok.nvalue in float64(range.a)..float64(range.b): + return ok(int(tok.nvalue)) + return err("Invalid integer") -func cssFloat(cval: CSSComponentValue): CSSFloat = +func cssFloat(cval: CSSComponentValue): Result[CSSFloat, string] = if isToken(cval): let tok = getToken(cval) if tok.tokenType == CSS_IDENT_TOKEN: case tok.value - of "none": return FLOAT_NONE - of "left": return FLOAT_LEFT - of "right": return FLOAT_RIGHT - raise newException(CSSValueError, "Invalid float") + of "none": return ok(FLOAT_NONE) + of "left": return ok(FLOAT_LEFT) + of "right": return ok(FLOAT_RIGHT) + return err("Invalid float") -func cssVisibility(cval: CSSComponentValue): CSSVisibility = +func cssVisibility(cval: CSSComponentValue): Result[CSSVisibility, string] = if isToken(cval): let tok = getToken(cval) if tok.tokenType == CSS_IDENT_TOKEN: case tok.value - of "visible": return VISIBILITY_VISIBLE - of "hidden": return VISIBILITY_HIDDEN - of "collapse": return VISIBILITY_COLLAPSE - raise newException(CSSValueError, "Invalid visibility") + of "visible": return ok(VISIBILITY_VISIBLE) + of "hidden": return ok(VISIBILITY_HIDDEN) + of "collapse": return ok(VISIBILITY_COLLAPSE) + return err("Invalid visibility") -proc getValueFromDecl(val: CSSComputedValue, d: CSSDeclaration, vtype: CSSValueType, ptype: CSSPropertyType) = +proc getValueFromDecl(val: CSSComputedValue, d: CSSDeclaration, + vtype: CSSValueType, ptype: CSSPropertyType): Err[string] = var i = 0 d.value.skipWhitespace(i) if i >= d.value.len: - raise newException(CSSValueError, "Empty value") + return err("Empty value") let cval = d.value[i] inc i case vtype of VALUE_COLOR: - val.color = cssColor(cval) + val.color = ?cssColor(cval) of VALUE_LENGTH: case ptype of PROPERTY_WORD_SPACING: - val.length = cssWordSpacing(cval) + val.length = ?cssWordSpacing(cval) of PROPERTY_LINE_HEIGHT: - val.length = cssLineHeight(cval) + val.length = ?cssLineHeight(cval) of PROPERTY_MAX_WIDTH, PROPERTY_MAX_HEIGHT, PROPERTY_MIN_WIDTH, PROPERTY_MIN_HEIGHT: - val.length = cssMaxMinSize(cval) + val.length = ?cssMaxMinSize(cval) of PROPERTY_PADDING_LEFT, PROPERTY_PADDING_RIGHT, PROPERTY_PADDING_TOP, PROPERTY_PADDING_BOTTOM: - val.length = cssLength(cval, has_auto = false) + val.length = ?cssLength(cval, has_auto = false) else: - val.length = cssLength(cval) - of VALUE_FONT_STYLE: val.fontstyle = cssFontStyle(cval) - of VALUE_DISPLAY: val.display = cssDisplay(cval) - of VALUE_CONTENT: val.content = cssContent(d) - of VALUE_WHITE_SPACE: val.whitespace = cssWhiteSpace(cval) + val.length = ?cssLength(cval) + of VALUE_FONT_STYLE: + val.fontstyle = ?cssFontStyle(cval) + of VALUE_DISPLAY: + val.display = ?cssDisplay(cval) + of VALUE_CONTENT: + val.content = cssContent(d) + of VALUE_WHITE_SPACE: + val.whitespace = ?cssWhiteSpace(cval) of VALUE_INTEGER: if ptype == PROPERTY_FONT_WEIGHT: - val.integer = cssFontWeight(cval) + val.integer = ?cssFontWeight(cval) elif ptype == PROPERTY_CHA_COLSPAN: - val.integer = cssInteger(cval, 1 .. 1000) + val.integer = ?cssInteger(cval, 1 .. 1000) elif ptype == PROPERTY_CHA_ROWSPAN: - val.integer = cssInteger(cval, 0 .. 65534) - of VALUE_TEXT_DECORATION: val.textdecoration = cssTextDecoration(d) - of VALUE_WORD_BREAK: val.wordbreak = cssWordBreak(cval) - of VALUE_LIST_STYLE_TYPE: val.liststyletype = cssListStyleType(cval) - of VALUE_VERTICAL_ALIGN: val.verticalalign = cssVerticalAlign(cval) - of VALUE_TEXT_ALIGN: val.textalign = cssTextAlign(cval) - of VALUE_LIST_STYLE_POSITION: val.liststyleposition = cssListStylePosition(cval) - of VALUE_POSITION: val.position = cssPosition(cval) - of VALUE_CAPTION_SIDE: val.captionside = cssCaptionSide(cval) - of VALUE_BORDER_COLLAPSE: val.bordercollapse = cssBorderCollapse(cval) + val.integer = ?cssInteger(cval, 0 .. 65534) + of VALUE_TEXT_DECORATION: + val.textdecoration = cssTextDecoration(d) + of VALUE_WORD_BREAK: + val.wordbreak = ?cssWordBreak(cval) + of VALUE_LIST_STYLE_TYPE: + val.liststyletype = ?cssListStyleType(cval) + of VALUE_VERTICAL_ALIGN: + val.verticalalign = ?cssVerticalAlign(cval) + of VALUE_TEXT_ALIGN: + val.textalign = ?cssTextAlign(cval) + of VALUE_LIST_STYLE_POSITION: + val.liststyleposition = ?cssListStylePosition(cval) + of VALUE_POSITION: + val.position = ?cssPosition(cval) + of VALUE_CAPTION_SIDE: + val.captionside = ?cssCaptionSide(cval) + of VALUE_BORDER_COLLAPSE: + val.bordercollapse = ?cssBorderCollapse(cval) of VALUE_LENGTH2: - val.length2.a = cssAbsoluteLength(cval) + val.length2.a = ?cssAbsoluteLength(cval) d.value.skipWhitespace(i) if i >= d.value.len: val.length2.b = val.length2.a else: let cval = d.value[i] - val.length2.b = cssAbsoluteLength(cval) - of VALUE_QUOTES: val.quotes = cssQuotes(d) - of VALUE_COUNTER_RESET: val.counterreset = cssCounterReset(d) - of VALUE_IMAGE: val.image = cssImage(cval) - of VALUE_FLOAT: val.float = cssFloat(cval) - of VALUE_VISIBILITY: val.visibility = cssVisibility(cval) - of VALUE_NONE: discard + val.length2.b = ?cssAbsoluteLength(cval) + of VALUE_QUOTES: + val.quotes = ?cssQuotes(d) + of VALUE_COUNTER_RESET: + val.counterreset = ?cssCounterReset(d) + of VALUE_IMAGE: + val.image = ?cssImage(cval) + of VALUE_FLOAT: + val.float = ?cssFloat(cval) + of VALUE_VISIBILITY: + val.visibility = ?cssVisibility(cval) + of VALUE_NONE: + discard + return ok() func getInitialColor(t: CSSPropertyType): RGBAColor = case t @@ -1074,100 +1105,95 @@ let defaultTable = getInitialTable() template getDefault(t: CSSPropertyType): CSSComputedValue = {.cast(noSideEffect).}: defaultTable[t] -# WARNING: may raise an exception. -func getComputedValue(d: CSSDeclaration, ptype: CSSPropertyType, vtype: CSSValueType): (CSSComputedValue, CSSGlobalValueType) = +func getComputedValue(d: CSSDeclaration, ptype: CSSPropertyType, + vtype: CSSValueType): + Result[(CSSComputedValue, CSSGlobalValueType), string] = + let global = cssGlobal(d) var val = CSSComputedValue(t: ptype, v: vtype) - try: - val.getValueFromDecl(d, vtype, ptype) - except CSSValueError: - let global = cssGlobal(d) + let r = val.getValueFromDecl(d, vtype, ptype) + if r.isErr: if global != VALUE_NOGLOBAL: - return (val, cssGlobal(d)) + return ok((val, global)) else: - raise - return (val, cssGlobal(d)) + return err(r.error) + return ok((val, global)) -func lengthShorthand(d: CSSDeclaration, props: array[4, CSSPropertyType]): seq[(CSSComputedValue, CSSGlobalValueType)] = +func lengthShorthand(d: CSSDeclaration, props: array[4, CSSPropertyType]): + Result[seq[(CSSComputedValue, CSSGlobalValueType)], string] = var i = 0 var cvals: seq[CSSComponentValue] while i < d.value.len: if d.value[i] != CSS_WHITESPACE_TOKEN: cvals.add(d.value[i]) inc i + var res: seq[(CSSComputedValue, CSSGlobalValueType)] case cvals.len of 1: # top, bottom, left, right - try: - for ptype in props: - let vtype = valueType(ptype) - let val = CSSComputedValue(t: ptype, v: vtype) - val.getValueFromDecl(d, vtype, ptype) - result.add((val, cssGlobal(d))) - except CSSValueError: discard + for ptype in props: + let vtype = valueType(ptype) + let val = CSSComputedValue(t: ptype, v: vtype) + ?val.getValueFromDecl(d, vtype, ptype) + res.add((val, cssGlobal(d))) of 2: # top, bottom | left, right - try: - for i in 0 ..< props.len: - let ptype = props[i] - let vtype = valueType(ptype) - let val = CSSComputedValue(t: ptype, v: vtype) - val.length = cssLength(cvals[i mod 2]) - result.add((val, cssGlobal(d))) - except CSSValueError: - discard + for i in 0 ..< props.len: + let ptype = props[i] + let vtype = valueType(ptype) + let val = CSSComputedValue(t: ptype, v: vtype) + val.length = ?cssLength(cvals[i mod 2]) + res.add((val, cssGlobal(d))) of 3: # top | left, right | bottom - try: - for i in 0 ..< props.len: - let ptype = props[i] - let vtype = valueType(ptype) - let val = CSSComputedValue(t: ptype, v: vtype) - let j = if i == 0: - 0 # top - elif i == 3: - 2 # bottom - else: - 1 # left, right - val.length = cssLength(cvals[j]) - result.add((val, cssGlobal(d))) - except CSSValueError: - discard + for i in 0 ..< props.len: + let ptype = props[i] + let vtype = valueType(ptype) + let val = CSSComputedValue(t: ptype, v: vtype) + let j = if i == 0: + 0 # top + elif i == 3: + 2 # bottom + else: + 1 # left, right + val.length = ?cssLength(cvals[j]) + res.add((val, cssGlobal(d))) of 4: # top | right | bottom | left - try: - for i in 0 ..< props.len: - let ptype = props[i] - let vtype = valueType(ptype) - let val = CSSComputedValue(t: ptype, v: vtype) - val.length = cssLength(cvals[i]) - result.add((val, cssGlobal(d))) - except CSSValueError: - discard + for i in 0 ..< props.len: + let ptype = props[i] + let vtype = valueType(ptype) + let val = CSSComputedValue(t: ptype, v: vtype) + val.length = ?cssLength(cvals[i]) + res.add((val, cssGlobal(d))) else: discard + return ok(res) + +const PropertyMarginSpec = [ + PROPERTY_MARGIN_TOP, PROPERTY_MARGIN_RIGHT, PROPERTY_MARGIN_BOTTOM, + PROPERTY_MARGIN_LEFT +] + +const PropertyPaddingSpec = [ + PROPERTY_PADDING_TOP, PROPERTY_PADDING_RIGHT, PROPERTY_PADDING_BOTTOM, + PROPERTY_PADDING_LEFT +] -proc getComputedValues(d: CSSDeclaration): seq[(CSSComputedValue, CSSGlobalValueType)] = +proc getComputedValues0(d: CSSDeclaration): + Result[seq[(CSSComputedValue, CSSGlobalValueType)], string] = let name = d.name + var res: seq[(CSSComputedValue, CSSGlobalValueType)] case shorthandType(name) of SHORTHAND_NONE: let ptype = propertyType(name) let vtype = valueType(ptype) - try: - result.add(getComputedValue(d, ptype, vtype)) - except CSSValueError: - discard + res.add(?getComputedValue(d, ptype, vtype)) of SHORTHAND_ALL: let global = cssGlobal(d) if global != VALUE_NOGLOBAL: for ptype in CSSPropertyType: let vtype = valueType(ptype) let val = CSSComputedValue(t: ptype, v: vtype) - result.add((val, global)) + res.add((val, global)) of SHORTHAND_MARGIN: - result.add(lengthShorthand(d, [PROPERTY_MARGIN_TOP, - PROPERTY_MARGIN_RIGHT, - PROPERTY_MARGIN_BOTTOM, - PROPERTY_MARGIN_LEFT])) + res.add(?lengthShorthand(d, PropertyMarginSpec)) of SHORTHAND_PADDING: - result.add(lengthShorthand(d, [PROPERTY_PADDING_TOP, - PROPERTY_PADDING_RIGHT, - PROPERTY_PADDING_BOTTOM, - PROPERTY_PADDING_LEFT])) + res.add(?lengthShorthand(d, PropertyPaddingSpec)) of SHORTHAND_BACKGROUND: let global = cssGlobal(d) let bgcolorptype = PROPERTY_BACKGROUND_COLOR @@ -1178,19 +1204,17 @@ proc getComputedValues(d: CSSDeclaration): seq[(CSSComputedValue, CSSGlobalValue for tok in d.value: if tok == CSS_WHITESPACE_TOKEN: continue - try: - let img = cssImage(tok) - bgimageval.image = img - result.add((bgimageval, global)) - except CSSValueError: - try: - let color = cssColor(tok) - bgcolorval.color = color - result.add((bgcolorval, global)) - except CSSValueError: - discard + let r = cssImage(tok) + if r.isOk: + bgimageval.image = r.get + res.add((bgimageval, global)) + else: + let r = cssColor(tok) + if r.isOk: + bgcolorval.color = r.get + res.add((bgcolorval, global)) else: - result.add((bgcolorval, global)) + res.add((bgcolorval, global)) of SHORTHAND_LIST_STYLE: let global = cssGlobal(d) let positionptype = PROPERTY_LIST_STYLE_POSITION @@ -1199,19 +1223,28 @@ proc getComputedValues(d: CSSDeclaration): seq[(CSSComputedValue, CSSGlobalValue let typeval = CSSComputedValue(t: typeptype, v: valueType(typeptype)) if global == VALUE_NOGLOBAL: for tok in d.value: - try: - positionval.liststyleposition = cssListStylePosition(tok) - result.add((positionval, global)) - except CSSValueError: - try: - typeval.liststyletype = cssListStyleType(tok) - result.add((typeval, global)) - except CSSValueError: + let r = cssListStylePosition(tok) + if r.isOk: + positionval.liststyleposition = r.get + res.add((positionval, global)) + else: + let r = cssListStyleType(tok) + if r.isOk: + typeval.liststyletype = r.get + res.add((typeval, global)) + else: #TODO list-style-image discard + return ok(res) + +proc getComputedValues(d: CSSDeclaration): + seq[(CSSComputedValue, CSSGlobalValueType)] = + return getComputedValues0(d).get(@[]) proc newComputedValueBuilder*(parent: CSSComputedValues): CSSComputedValuesBuilder = - result.parent = parent + return CSSComputedValuesBuilder( + parent: parent + ) proc addValuesImportant*(builder: var CSSComputedValuesBuilder, decls: seq[CSSDeclaration], origin: CSSOrigin) = for decl in decls: |