Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 15 additions & 29 deletions src/parse-anplusb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export class ANplusBParser {
this.lexer.pos = start
this.lexer.line = line

let a: string | null = null
let b: string | null = null
let a_start = start
let a_end = start
Expand All @@ -56,11 +55,9 @@ export class ANplusBParser {
const text = this.source.substring(this.lexer.token_start, this.lexer.token_end).toLowerCase()

if (text === 'odd' || text === 'even') {
// Store the keyword as authored
a = this.source.substring(this.lexer.token_start, this.lexer.token_end)
a_start = this.lexer.token_start
a_end = this.lexer.token_end
return this.create_anplusb_node(node_start, a, null, a_start, a_end, 0, 0)
return this.create_anplusb_node(node_start, a_start, a_end, 0, 0)
}

// Check if it's 'n', '-n', or starts with 'n'
Expand All @@ -74,18 +71,15 @@ export class ANplusBParser {
const third_char = this.source.charCodeAt(this.lexer.token_start + 2)
if (third_char === CHAR_MINUS_HYPHEN /* - */) {
// -n-5 pattern
a = '-n'
a_start = this.lexer.token_start
a_end = this.lexer.token_start + 2
b = this.source.substring(this.lexer.token_start + 2, this.lexer.token_end)
b_start = this.lexer.token_start + 2
b_end = this.lexer.token_end
return this.create_anplusb_node(node_start, a, b, a_start, a_end, b_start, b_end)
return this.create_anplusb_node(node_start, a_start, a_end, b_start, b_end)
}
}

// Store -n as authored
a = '-n'
a_start = this.lexer.token_start
a_end = this.lexer.token_start + 2

Expand All @@ -95,7 +89,7 @@ export class ANplusBParser {
b_start = this.lexer.token_start
b_end = this.lexer.token_end
}
return this.create_anplusb_node(node_start, a, b, a_start, a_end, b_start, b_end)
return this.create_anplusb_node(node_start, a_start, a_end, b !== null ? b_start : 0, b !== null ? b_end : 0)
}

// n, n+3, n-5
Expand All @@ -105,18 +99,16 @@ export class ANplusBParser {
const second_char = this.source.charCodeAt(this.lexer.token_start + 1)
if (second_char === CHAR_MINUS_HYPHEN /* - */) {
// n-5 pattern
a = 'n'
// a = 'n'
a_start = this.lexer.token_start
a_end = this.lexer.token_start + 1
b = this.source.substring(this.lexer.token_start + 1, this.lexer.token_end)
b_start = this.lexer.token_start + 1
b_end = this.lexer.token_end
return this.create_anplusb_node(node_start, a, b, a_start, a_end, b_start, b_end)
return this.create_anplusb_node(node_start, a_start, a_end, b_start, b_end)
}
}

// Store n as authored
a = 'n'
a_start = this.lexer.token_start
a_end = this.lexer.token_start + 1

Expand All @@ -126,7 +118,7 @@ export class ANplusBParser {
b_start = this.lexer.token_start
b_end = this.lexer.token_end
}
return this.create_anplusb_node(node_start, a, b, a_start, a_end, b_start, b_end)
return this.create_anplusb_node(node_start, a_start, a_end, b !== null ? b_start : 0, b !== null ? b_end : 0)
}

// Not a valid An+B pattern
Expand All @@ -144,8 +136,6 @@ export class ANplusBParser {
const first_char = text.charCodeAt(0)

if (first_char === 0x6e /* n */) {
// Store +n as authored (including the +)
a = '+n'
a_start = saved.pos - 1 // Position of the + delim
a_end = this.lexer.token_start + 1

Expand All @@ -157,7 +147,7 @@ export class ANplusBParser {
b = this.source.substring(this.lexer.token_start + 1, this.lexer.token_end)
b_start = this.lexer.token_start + 1
b_end = this.lexer.token_end
return this.create_anplusb_node(node_start, a, b, a_start, a_end, b_start, b_end)
return this.create_anplusb_node(node_start, a_start, a_end, b_start, b_end)
}
}

Expand All @@ -167,7 +157,7 @@ export class ANplusBParser {
b_start = this.lexer.token_start
b_end = this.lexer.token_end
}
return this.create_anplusb_node(node_start, a, b, a_start, a_end, b_start, b_end)
return this.create_anplusb_node(node_start, a_start, a_end, b !== null ? b_start : 0, b !== null ? b_end : 0)
}
}

Expand All @@ -180,8 +170,6 @@ export class ANplusBParser {
const n_index = token_text.toLowerCase().indexOf('n')

if (n_index !== -1) {
// Store 'a' coefficient including the 'n'
a = token_text.substring(0, n_index + 1)
a_start = this.lexer.token_start
a_end = this.lexer.token_start + n_index + 1

Expand All @@ -194,7 +182,7 @@ export class ANplusBParser {
b = remainder
b_start = this.lexer.token_start + n_index + 1
b_end = this.lexer.token_end
return this.create_anplusb_node(node_start, a, b, a_start, a_end, b_start, b_end)
return this.create_anplusb_node(node_start, a_start, a_end, b_start, b_end)
}
}

Expand All @@ -204,7 +192,7 @@ export class ANplusBParser {
b_start = this.lexer.token_start
b_end = this.lexer.token_end
}
return this.create_anplusb_node(node_start, a, b, a_start, a_end, b_start, b_end)
return this.create_anplusb_node(node_start, a_start, a_end, b_start, b_end)
}
}

Expand All @@ -214,7 +202,7 @@ export class ANplusBParser {
b = num_text
b_start = this.lexer.token_start
b_end = this.lexer.token_end
return this.create_anplusb_node(node_start, a, b, a_start, a_end, b_start, b_end)
return this.create_anplusb_node(node_start, 0, 0, b_start, b_end)
}

return null
Expand Down Expand Up @@ -278,8 +266,6 @@ export class ANplusBParser {

private create_anplusb_node(
start: number,
a: string | null,
b: string | null,
a_start: number,
a_end: number,
b_start: number,
Expand All @@ -291,14 +277,14 @@ export class ANplusBParser {
this.arena.set_length(node, this.lexer.pos - start)
this.arena.set_start_line(node, this.lexer.line)

// Store 'a' coefficient in content fields
if (a !== null) {
// Store 'a' coefficient in content fields if it exists (length > 0)
if (a_end > a_start) {
this.arena.set_content_start(node, a_start)
this.arena.set_content_length(node, a_end - a_start)
}

// Store 'b' coefficient in value fields
if (b !== null) {
// Store 'b' coefficient in value fields if it exists (length > 0)
if (b_end > b_start) {
this.arena.set_value_start(node, b_start)
this.arena.set_value_length(node, b_end - b_start)
}
Expand Down
17 changes: 7 additions & 10 deletions src/parse-atrule-prelude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,11 @@ export class AtRulePreludeParser {

// Skip comma separator
this.skip_whitespace()
if (this.peek_token_type() === TOKEN_COMMA) {
this.next_token() // consume comma
const saved = this.lexer.save_position()
this.next_token()
if (this.lexer.token_type !== TOKEN_COMMA) {
// Not a comma, restore position
this.lexer.restore_position(saved)
}
}

Expand All @@ -108,14 +111,14 @@ export class AtRulePreludeParser {
}

private is_and_or_not(str: string): boolean {
if (str.length > 3 || str.length < 2) return false
// All logical operators are 2-3 chars: "and" (3), "or" (2), "not" (3)
// The str_equals calls will quickly reject strings of other lengths
return str_equals('and', str) || str_equals('or', str) || str_equals('not', str)
}

// Parse a single media query: screen and (min-width: 768px)
private parse_single_media_query(): number | null {
let query_start = this.lexer.pos
let query_line = this.lexer.line

// Skip whitespace
this.skip_whitespace()
Expand Down Expand Up @@ -191,7 +194,6 @@ export class AtRulePreludeParser {
// Parse media feature: (min-width: 768px)
private parse_media_feature(): number | null {
let feature_start = this.lexer.token_start // '(' position
let feature_line = this.lexer.token_line

// Find matching right paren
let depth = 1
Expand Down Expand Up @@ -229,7 +231,6 @@ export class AtRulePreludeParser {
private parse_container_query(): number[] {
let nodes: number[] = []
let query_start = this.lexer.pos
let query_line = this.lexer.line

// Parse components (identifiers, operators, features)
let components: number[] = []
Expand Down Expand Up @@ -292,7 +293,6 @@ export class AtRulePreludeParser {
// Feature query: (property: value)
if (token_type === TOKEN_LEFT_PAREN) {
let feature_start = this.lexer.token_start
let feature_line = this.lexer.token_line

// Find matching right paren
let depth = 1
Expand Down Expand Up @@ -438,7 +438,6 @@ export class AtRulePreludeParser {
// For url() function, we need to consume all tokens until the closing paren
let url_start = this.lexer.token_start
let url_end = this.lexer.token_end
let url_line = this.lexer.token_line

if (this.lexer.token_type === TOKEN_FUNCTION) {
// It's url( ... we need to find the matching )
Expand Down Expand Up @@ -481,7 +480,6 @@ export class AtRulePreludeParser {
if (str_equals('layer', text)) {
let layer_start = this.lexer.token_start
let layer_end = this.lexer.token_end
let layer_line = this.lexer.token_line
let content_start = 0
let content_length = 0

Expand Down Expand Up @@ -539,7 +537,6 @@ export class AtRulePreludeParser {
let text = this.source.substring(this.lexer.token_start, this.lexer.token_end - 1) // -1 to exclude '('
if (str_equals('supports', text)) {
let supports_start = this.lexer.token_start
let supports_line = this.lexer.token_line

// Find matching closing parenthesis
let paren_depth = 1
Expand Down
40 changes: 20 additions & 20 deletions src/parse-selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ import {
CHAR_SINGLE_QUOTE,
CHAR_DOUBLE_QUOTE,
CHAR_COLON,
CHAR_CARRIAGE_RETURN,
CHAR_FORM_FEED,
CHAR_NEWLINE,
CHAR_SPACE,
} from './string-utils'
import { ANplusBParser } from './parse-anplusb'
import { CSSNode } from './css-node'
Expand Down Expand Up @@ -113,8 +117,10 @@ export class SelectorParser {

// Find the last component in the chain
let last_component = complex_selector
while (this.arena.get_next_sibling(last_component) !== 0) {
last_component = this.arena.get_next_sibling(last_component)
let next_sibling = this.arena.get_next_sibling(last_component)
while (next_sibling !== 0) {
last_component = next_sibling
next_sibling = this.arena.get_next_sibling(last_component)
}

// Set the complex selector chain as children
Expand Down Expand Up @@ -199,8 +205,6 @@ export class SelectorParser {
}

while (this.lexer.pos < this.selector_end) {
if (this.lexer.pos >= this.selector_end) break

// Parse compound selector first
let compound = this.parse_compound_selector()
if (compound !== null) {
Expand Down Expand Up @@ -347,22 +351,15 @@ export class SelectorParser {

// Parse the local part after | in a namespace selector (E or *)
// Returns the node type (TYPE or UNIVERSAL) or null if invalid
private parse_namespace_local_part(
selector_start: number,
namespace_start: number,
namespace_length: number,
): number | null {
private parse_namespace_local_part(selector_start: number, namespace_start: number, namespace_length: number): number | null {
const saved = this.lexer.save_position()
this.lexer.next_token_fast(false)

let node_type: number
if (this.lexer.token_type === TOKEN_IDENT) {
// ns|type
node_type = NODE_SELECTOR_TYPE
} else if (
this.lexer.token_type === TOKEN_DELIM &&
this.source.charCodeAt(this.lexer.token_start) === CHAR_ASTERISK
) {
} else if (this.lexer.token_type === TOKEN_DELIM && this.source.charCodeAt(this.lexer.token_start) === CHAR_ASTERISK) {
// ns|*
node_type = NODE_SELECTOR_UNIVERSAL
} else {
Expand Down Expand Up @@ -425,7 +422,8 @@ export class SelectorParser {
// Skip whitespace and check for combinator
while (this.lexer.pos < this.selector_end) {
let ch = this.source.charCodeAt(this.lexer.pos)
if (is_whitespace(ch)) {
// no calling is_whitespace() because of function call overhead
if (ch === CHAR_SPACE || ch === CHAR_NEWLINE || ch === CHAR_CARRIAGE_RETURN || ch === CHAR_FORM_FEED) {
has_whitespace = true
this.lexer.pos++
} else {
Expand All @@ -452,7 +450,8 @@ export class SelectorParser {
this.lexer.pos = whitespace_start
while (this.lexer.pos < this.selector_end) {
let ch = this.source.charCodeAt(this.lexer.pos)
if (is_whitespace(ch)) {
// no calling is_whitespace() because of function call overhead
if (ch === CHAR_SPACE || ch === CHAR_NEWLINE || ch === CHAR_CARRIAGE_RETURN || ch === CHAR_FORM_FEED) {
this.lexer.pos++
} else {
break
Expand Down Expand Up @@ -565,28 +564,29 @@ export class SelectorParser {

// Parse operator
let ch1 = this.source.charCodeAt(pos)
let ch2 = pos + 1 < end ? this.source.charCodeAt(pos + 1) : 0 // Cache second character to avoid repeated calls

if (ch1 === CHAR_EQUALS) {
// =
operator_end = pos + 1
this.arena.set_attr_operator(node, ATTR_OPERATOR_EQUAL)
} else if (ch1 === CHAR_TILDE && pos + 1 < end && this.source.charCodeAt(pos + 1) === CHAR_EQUALS) {
} else if (ch1 === CHAR_TILDE && ch2 === CHAR_EQUALS) {
// ~=
operator_end = pos + 2
this.arena.set_attr_operator(node, ATTR_OPERATOR_TILDE_EQUAL)
} else if (ch1 === CHAR_PIPE && pos + 1 < end && this.source.charCodeAt(pos + 1) === CHAR_EQUALS) {
} else if (ch1 === CHAR_PIPE && ch2 === CHAR_EQUALS) {
// |=
operator_end = pos + 2
this.arena.set_attr_operator(node, ATTR_OPERATOR_PIPE_EQUAL)
} else if (ch1 === CHAR_CARET && pos + 1 < end && this.source.charCodeAt(pos + 1) === CHAR_EQUALS) {
} else if (ch1 === CHAR_CARET && ch2 === CHAR_EQUALS) {
// ^=
operator_end = pos + 2
this.arena.set_attr_operator(node, ATTR_OPERATOR_CARET_EQUAL)
} else if (ch1 === CHAR_DOLLAR && pos + 1 < end && this.source.charCodeAt(pos + 1) === CHAR_EQUALS) {
} else if (ch1 === CHAR_DOLLAR && ch2 === CHAR_EQUALS) {
// $=
operator_end = pos + 2
this.arena.set_attr_operator(node, ATTR_OPERATOR_DOLLAR_EQUAL)
} else if (ch1 === CHAR_ASTERISK && pos + 1 < end && this.source.charCodeAt(pos + 1) === CHAR_EQUALS) {
} else if (ch1 === CHAR_ASTERISK && ch2 === CHAR_EQUALS) {
// *=
operator_end = pos + 2
this.arena.set_attr_operator(node, ATTR_OPERATOR_STAR_EQUAL)
Expand Down
Loading
Loading