// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
priv enum ParserToken {
At(Int, Int, Int)
LParen(Int, Int, Int)
RParen(Int, Int, Int)
LBrace(Int, Int, Int)
RBrace(Int, Int, Int)
LBracket(Int, Int, Int)
RBracket(Int, Int, Int)
Comma(Int, Int, Int)
Semi(Int, Int, Int)
Eq(Int, Int, Int)
Lt(Int, Int, Int)
Gt(Int, Int, Int)
DoubleColon(Int, Int, Int)
Ident(String, Int, Int, Int)
Other(Int, Int, Int)
}
///|
fn ParserToken::start(self : ParserToken) -> Int {
match self {
At(start, _, _) => start
LParen(start, _, _) => start
RParen(start, _, _) => start
LBrace(start, _, _) => start
RBrace(start, _, _) => start
LBracket(start, _, _) => start
RBracket(start, _, _) => start
Comma(start, _, _) => start
Semi(start, _, _) => start
Eq(start, _, _) => start
Lt(start, _, _) => start
Gt(start, _, _) => start
DoubleColon(start, _, _) => start
Ident(_, start, _, _) => start
Other(start, _, _) => start
}
}
///|
fn ParserToken::end(self : ParserToken) -> Int {
match self {
At(_, end_, _) => end_
LParen(_, end_, _) => end_
RParen(_, end_, _) => end_
LBrace(_, end_, _) => end_
RBrace(_, end_, _) => end_
LBracket(_, end_, _) => end_
RBracket(_, end_, _) => end_
Comma(_, end_, _) => end_
Semi(_, end_, _) => end_
Eq(_, end_, _) => end_
Lt(_, end_, _) => end_
Gt(_, end_, _) => end_
DoubleColon(_, end_, _) => end_
Ident(_, _, end_, _) => end_
Other(_, end_, _) => end_
}
}
///|
fn ParserToken::line(self : ParserToken) -> Int {
match self {
At(_, _, line) => line
LParen(_, _, line) => line
RParen(_, _, line) => line
LBrace(_, _, line) => line
RBrace(_, _, line) => line
LBracket(_, _, line) => line
RBracket(_, _, line) => line
Comma(_, _, line) => line
Semi(_, _, line) => line
Eq(_, _, line) => line
Lt(_, _, line) => line
Gt(_, _, line) => line
DoubleColon(_, _, line) => line
Ident(_, _, _, line) => line
Other(_, _, line) => line
}
}
///|
fn parser_is_identifier_start(code : Int) -> Bool {
@lex.wgsl_identifier_start_code_point(code)
}
///|
fn parser_is_identifier_char(code : Int) -> Bool {
@lex.wgsl_identifier_continue_code_point(code)
}
///|
fn parser_identifier_start_width(source : String, index : Int) -> Int {
match @lex.wgsl_code_point_at(source, index) {
Some((code_point, width)) if parser_is_identifier_start(code_point) => width
_ => 0
}
}
///|
fn parser_identifier_continue_width(source : String, index : Int) -> Int {
match @lex.wgsl_code_point_at(source, index) {
Some((code_point, width)) if parser_is_identifier_char(code_point) => width
_ => 0
}
}
///|
fn parser_identifier_continues_before(source : String, index : Int) -> Bool {
guard index > 0 else { return false }
let previous = index - 1
let previous_code = source.code_unit_at(previous).to_int()
let start = if previous_code >= 0xDC00 && previous_code <= 0xDFFF {
previous - 1
} else {
previous
}
start >= 0 && parser_identifier_continue_width(source, start) == index - start
}
///|
fn parser_is_reserved_word(name : String) -> Bool {
@ir.wgsl_ir_identifier_is_reserved(name) &&
!@ir.wgsl_ir_identifier_is_predeclared_type(name)
}
///|
fn parser_validate_declaration_ident(
name : String,
) -> Unit raise WeslCompileError {
if !@lex.wgsl_identifier_text(name) {
raise Parse("invalid declaration identifier `\{name}`")
}
if name == "_" {
raise Parse("invalid declaration identifier `\{name}`")
}
if name.length() >= 2 && name[:2].to_owned() == "__" {
raise Parse("invalid declaration identifier `\{name}`")
}
if parser_is_reserved_word(name) {
raise Parse("invalid declaration identifier `\{name}`")
}
}
///|
fn parser_lex(source : String) -> Array[ParserToken] {
let tokens : Array[ParserToken] = []
let mut index = 0
let mut line = 1
while index < source.length() {
let code = source.code_unit_at(index).to_int()
if code == 32 || code == 9 || code == 13 {
index += 1
continue
}
if code == 10 {
line += 1
index += 1
continue
}
if code == 47 &&
index + 1 < source.length() &&
source.code_unit_at(index + 1).to_int() == 47 {
index += 2
while index < source.length() && source.code_unit_at(index).to_int() != 10 {
index += 1
}
continue
}
if code == 47 &&
index + 1 < source.length() &&
source.code_unit_at(index + 1).to_int() == 42 {
index += 2
while index + 1 < source.length() {
if source.code_unit_at(index).to_int() == 10 {
line += 1
}
if source.code_unit_at(index).to_int() == 42 &&
source.code_unit_at(index + 1).to_int() == 47 {
index += 2
break
}
index += 1
}
continue
}
if code == 58 &&
index + 1 < source.length() &&
source.code_unit_at(index + 1).to_int() == 58 {
tokens.push(DoubleColon(index, index + 2, line))
index += 2
continue
}
let identifier_width = parser_identifier_start_width(source, index)
if identifier_width > 0 {
let start = index
index += identifier_width
while index < source.length() {
let width = parser_identifier_continue_width(source, index)
if width == 0 {
break
}
index += width
}
tokens.push(Ident(source[start:index].to_owned(), start, index, line))
continue
}
match code {
64 => tokens.push(At(index, index + 1, line))
40 => tokens.push(LParen(index, index + 1, line))
41 => tokens.push(RParen(index, index + 1, line))
123 => tokens.push(LBrace(index, index + 1, line))
125 => tokens.push(RBrace(index, index + 1, line))
91 => tokens.push(LBracket(index, index + 1, line))
93 => tokens.push(RBracket(index, index + 1, line))
44 => tokens.push(Comma(index, index + 1, line))
59 => tokens.push(Semi(index, index + 1, line))
61 => tokens.push(Eq(index, index + 1, line))
60 => tokens.push(Lt(index, index + 1, line))
62 => tokens.push(Gt(index, index + 1, line))
_ => tokens.push(Other(index, index + 1, line))
}
index += 1
}
tokens
}
///|
fn[T] ArrayView::start(self : ArrayView[T]) -> Int = "%arrayview.start"
///|
priv struct Parser {
source : String
tokens : Array[ParserToken]
mut position : Int
}
///|
fn Parser::new(source : String) -> Parser {
{ source, tokens: parser_lex(source), position: 0 }
}
///|
fn Parser::from_tokens(source : String, tokens : Array[ParserToken]) -> Parser {
{ source, tokens, position: 0 }
}
///|
fn Parser::view(self : Parser) -> ArrayView[ParserToken] {
self.tokens[self.position:]
}
///|
fn Parser::update_view(self : Parser, view : ArrayView[ParserToken]) -> Unit {
self.position = view.start()
}
///|
fn Parser::update_view_after_offset(self : Parser, offset : Int) -> Unit {
let mut position = self.position
while position < self.tokens.length() &&
self.tokens[position].start() < offset {
position += 1
}
self.position = position
}
///|
fn[A] Parser::error(
self : Parser,
message : String,
) -> A raise WeslCompileError {
match self.view() {
[token, ..] => raise Parse("\{message} at line \{token.line()}")
[] => raise Parse("\{message} at the end of input")
}
}
///|
fn Parser::source_slice(
self : Parser,
start_offset : Int,
end_offset : Int,
) -> String {
if start_offset >= end_offset {
""
} else {
self.source[start_offset:end_offset].to_owned()
}
}
///|
fn Parser::item_source(self : Parser) -> String raise WeslCompileError {
match self.tokens[:] {
[first, ..] =>
match self.tokens.last() {
Some(last) => self.source_slice(first.start(), last.end())
None => self.error("empty top-level item")
}
[] => self.error("empty top-level item")
}
}
///|
fn Parser::item_span(self : Parser) -> SyntaxSpan raise WeslCompileError {
match self.tokens[:] {
[first, ..] =>
match self.tokens.last() {
Some(last) => { start_line: first.line(), end_line: last.line() }
None => self.error("empty top-level item")
}
[] => self.error("empty top-level item")
}
}
///|
fn Parser::parse_attributes(
self : Parser,
) -> Array[Attribute] raise WeslCompileError {
let first_token = match self.view() {
[token, ..] => token
[] => return []
}
let item_source = self.item_source()
let parsed = @attribute_parse.parse_attributes_source(item_source)
let attrs : Array[Attribute] = []
let success = match parsed {
Parsed(success) => success
Failed(diagnostic) => {
let byte_offset = first_token.start() + diagnostic.offset
if diagnostic.incomplete {
raise Parse("incomplete attribute list at byte \{byte_offset}")
}
raise Parse("invalid attribute list at byte \{byte_offset}")
}
}
let line_base = first_token.line() - 1
for attr in success.attributes {
attrs.push({
name: attr.name,
arguments: attr.arguments,
argument_exprs: parser_parse_attribute_argument_exprs(
attr.name,
attr.arguments,
item_source,
),
condition_expr: parser_parse_attribute_condition_expr(
attr.name,
attr.arguments,
),
span: {
start_line: line_base + attr.start_line,
end_line: line_base + attr.end_line,
},
})
}
self.update_view_after_offset(first_token.start() + success.consumed_offset)
attrs
}
///|
fn parser_parse_attribute_argument_exprs(
name : String,
arguments : String?,
item_source : String,
) -> Array[Expression] raise WeslCompileError {
if name == "if" || name == "elif" || name == "else" {
return []
}
match arguments {
Some(text) => {
let expressions : Array[Expression] = []
for part in parser_split_top_level_commas(text) {
expressions.push(parser_parse_statement_expression(part, item_source))
}
expressions
}
None => []
}
}
///|
fn parser_cond_expression_from_generated(
node : @cond_expr_parse.CondExprNode,
) -> CondExpression {
match node {
Literal(value) => Literal(value)
Feature(name) => Feature(name)
Not(inner) => Not(parser_cond_expression_from_generated(inner))
And(left, right) =>
And(
parser_cond_expression_from_generated(left),
parser_cond_expression_from_generated(right),
)
Or(left, right) =>
Or(
parser_cond_expression_from_generated(left),
parser_cond_expression_from_generated(right),
)
}
}
///|
fn parser_parse_attribute_condition_expr(
name : String,
arguments : String?,
) -> CondExpression? {
if name != "if" && name != "elif" {
return None
}
match arguments {
Some(text) =>
match @cond_expr_parse.parse_cond_expr_source(text) {
Parsed(node) => Some(parser_cond_expression_from_generated(node))
Failed(_) => None
}
None => None
}
}
///|
fn Parser::item_is_import(self : Parser) -> Bool raise WeslCompileError {
let original_position = self.position
ignore(self.parse_attributes())
let result = match self.view() {
[Ident("import", _, _, _), ..] => true
_ => false
}
self.position = original_position
result
}
///|
fn parser_import_node_from_generated(
node : @import_parse.ImportParseNode,
) -> ImportNode {
let children : Array[ImportNode] = []
for child in node.children {
children.push(parser_import_node_from_generated(child))
}
{ path_segments: node.path_segments, rename: node.rename, children }
}
///|
fn parser_struct_members_from_wgsl(
source : String,
members : Array[@ast.WgslStructMember],
) -> Array[StructMember] raise WeslCompileError {
let mapped : Array[StructMember] = []
for field in members {
guard field.ty_ref() is Some(ty) else {
raise Parse("struct member `\{field.name().name()}` has no type")
}
let type_text = source[ty.start():ty.end()].trim().to_owned()
let attribute_text = source[field.start():field.name().start()]
.trim()
.to_owned()
mapped.push({
name: field.name().name(),
type_text,
type_expr: parser_type_expression_from_wgsl(source, ty),
attributes: if attribute_text == "" {
[]
} else {
parser_statement_attributes(attribute_text)
},
})
}
mapped
}
///|
fn parser_function_parameters_from_generated(
parameters : Array[@function_parse.FunctionParameterParseNode],
) -> Array[FunctionParameter] raise WeslCompileError {
let mapped : Array[FunctionParameter] = []
for parameter in parameters {
mapped.push({
name: parameter.name,
type_text: parameter.type_text,
type_expr: parser_parse_type_expression(parameter.type_text),
attributes: if parameter.attributes == "" {
[]
} else {
parser_statement_attributes(parameter.attributes)
},
})
}
mapped
}
///|
fn parser_statement_kind_from_generated(
kind : @statement_parse.StatementParseKind,
) -> StatementKind {
match kind {
Empty => Empty
Block => Block
Return => Return
Discard => Discard
If => If
Switch => Switch
Loop => Loop
For => For
While => While
Continuing => Continuing
Break => Break
Continue => Continue
BreakIf => BreakIf
ConstAssert => ConstAssert
Const => Const
Let => Let
Var => Var
Assignment => Assignment
CompoundAssignment => CompoundAssignment
Increment => Increment
Decrement => Decrement
Call => Call
Other => Other
}
}
///|
fn parser_statement_declaration_kind_from_binding(
kind : @binding_parse.BindingParseKind,
) -> StatementDeclarationKind raise WeslCompileError {
match kind {
Const => Const
Let => Let
Var => Var
_ => raise Parse("expected local declaration statement")
}
}
///|
fn parser_expression_type_source(
node : @expression_parse.ExpressionTypeParseNode,
) -> String {
let mut source = ""
for segment in node.path {
if source == "" {
source = segment
} else {
source = source + "::" + segment
}
}
if source == "" {
source = node.ident
} else {
source = source + "::" + node.ident
}
match node.template_text {
Some(template_text) => source + "<" + template_text + ">"
None => source
}
}
///|
fn parser_type_expression_from_generated(
node : @expression_parse.ExpressionTypeParseNode,
) -> TypeExpression raise WeslCompileError {
match node.template_text {
Some(template_text) => {
let parsed = parser_parse_type_expression(
parser_expression_type_source(node),
)
{
path: parsed.path,
ident: parsed.ident,
template_text: Some(template_text),
template_args: parsed.template_args,
}
}
None =>
{
path: node.path,
ident: node.ident,
template_text: None,
template_args: [],
}
}
}
///|
fn parser_type_expression_from_wgsl(
source : String,
node : @ast.WgslTypeRef,
) -> TypeExpression {
let parts : Array[String] = []
for part in node.name().split("::") {
parts.push(part.to_owned())
}
let path : Array[String] = []
for index in 0..<(parts.length() - 1) {
path.push(parts[index])
}
let template_args : Array[TypeTemplateArgument] = []
for arg in node.template_arguments() {
match arg {
Type(ty) if ty.atom().kind() == Numeric =>
template_args.push(Literal(ty.name()))
Type(ty) =>
template_args.push(Type(parser_type_expression_from_wgsl(source, ty)))
Expression(expr) =>
template_args.push(
Literal(source[expr.start():expr.end()].trim().to_owned()),
)
}
}
{ path, ident: parts[parts.length() - 1], template_text: None, template_args }
}
///|
fn parser_parse_type_expression(
source : String,
) -> TypeExpression raise WeslCompileError {
match @parser.parse_wgsl_type_ref(source) {
Some(node) => parser_type_expression_from_wgsl(source, node)
None => raise Parse("invalid type expression: \{source}")
}
}
///|
fn parser_parse_optional_type_expression(
source : String?,
) -> TypeExpression? raise WeslCompileError {
match source {
Some(text) => Some(parser_parse_type_expression(text))
None => None
}
}
///|
fn parser_parse_optional_initializer_expression(
source : String?,
item_source : String,
) -> Expression? raise WeslCompileError {
match source {
Some(text) => Some(parser_parse_statement_expression(text, item_source))
None => None
}
}
///|
fn parser_function_return_attributes(
source : String,
) -> Array[Attribute] raise WeslCompileError {
let mut arrow = -1
let mut index = 0
while index + 1 < source.length() {
let code = source.code_unit_at(index).to_int()
if code == 123 {
break
}
if code == 45 && source.code_unit_at(index + 1).to_int() == 62 {
arrow = index
break
}
index += 1
}
if arrow < 0 {
return []
}
let attr_start = parser_skip_layout_and_comments(source, arrow + 2)
let mut cursor = attr_start
while cursor < source.length() && source.code_unit_at(cursor).to_int() == 64 {
cursor += 1
while cursor < source.length() {
let width = parser_identifier_continue_width(source, cursor)
if width == 0 {
break
}
cursor += width
}
cursor = parser_skip_layout_and_comments(source, cursor)
if cursor < source.length() && source.code_unit_at(cursor).to_int() == 40 {
let close = match parser_find_matching_delimiter(source, cursor, 40, 41) {
Some(end_) => end_
None => raise Parse("incomplete function return attribute list")
}
cursor = close + 1
}
cursor = parser_skip_layout_and_comments(source, cursor)
}
if cursor == attr_start {
[]
} else {
parser_statement_attributes(source[attr_start:cursor].to_owned())
}
}
///|
fn parser_parse_const_assert_declaration(
item_source : String,
) -> ConstAssertDeclaration raise WeslCompileError {
let source = parser_trim_statement_semicolon(
parser_strip_statement_attributes(item_source),
)
let assertion = source[12:].trim().to_owned()
{
assertion,
assertion_expr: parser_parse_statement_expression(assertion, item_source),
}
}
///|
fn parser_diagnostic_directive_from_arguments(
arguments : Array[String],
) -> DiagnosticDirectiveDeclaration {
{ arguments, severity: arguments.get(0), rule_name: arguments.get(1) }
}
///|
fn parser_unary_operator_from_generated(
op : @expression_parse.ExpressionUnaryOperator,
) -> UnaryOperator {
match op {
Negation => Negation
LogicalNegation => LogicalNegation
BitwiseComplement => BitwiseComplement
Indirection => Indirection
AddressOf => AddressOf
}
}
///|
fn parser_binary_operator_from_generated(
op : @expression_parse.ExpressionBinaryOperator,
) -> BinaryOperator {
match op {
ShortCircuitOr => ShortCircuitOr
ShortCircuitAnd => ShortCircuitAnd
BitwiseOr => BitwiseOr
BitwiseXor => BitwiseXor
BitwiseAnd => BitwiseAnd
Equality => Equality
Inequality => Inequality
LessThan => LessThan
LessThanEqual => LessThanEqual
GreaterThan => GreaterThan
GreaterThanEqual => GreaterThanEqual
ShiftLeft => ShiftLeft
ShiftRight => ShiftRight
Addition => Addition
Subtraction => Subtraction
Multiplication => Multiplication
Division => Division
Remainder => Remainder
}
}
///|
fn parser_assignment_operator_from_text(
text : String,
) -> AssignmentOperator raise WeslCompileError {
match text {
"=" => Equal
"+=" => PlusEqual
"-=" => MinusEqual
"*=" => TimesEqual
"/=" => DivisionEqual
"%=" => ModuloEqual
"&=" => AndEqual
"|=" => OrEqual
"^=" => XorEqual
"<<=" => ShiftLeftAssign
">>=" => ShiftRightAssign
_ => raise Parse("invalid assignment operator `\{text}`")
}
}
///|
fn parser_expression_from_generated(
node : @expression_parse.ExpressionParseNode,
) -> Expression raise WeslCompileError {
match node {
Literal(text) => Literal(text)
Bool(value) => Bool(value)
TypeOrIdentifier(ty) =>
TypeOrIdentifier(parser_type_expression_from_generated(ty))
Parenthesized(expr) => Parenthesized(parser_expression_from_generated(expr))
NamedComponent(base, component) =>
NamedComponent(parser_expression_from_generated(base), component)
Indexing(base, index) =>
Indexing(
parser_expression_from_generated(base),
parser_expression_from_generated(index),
)
Unary(op, operand) =>
Unary(
parser_unary_operator_from_generated(op),
parser_expression_from_generated(operand),
)
Binary(op, left, right) =>
Binary(
parser_binary_operator_from_generated(op),
parser_expression_from_generated(left),
parser_expression_from_generated(right),
)
FunctionCall(call) => {
let arguments : Array[Expression] = []
for argument in call.arguments {
arguments.push(parser_expression_from_generated(argument))
}
FunctionCall({
callee: parser_type_expression_from_generated(call.callee),
arguments,
})
}
}
}
///|
fn parser_assignment_operator_at_top_level(
source : String,
) -> (String, Int, Int)? {
let mut cursor = 0
let mut paren_depth = 0
let mut bracket_depth = 0
let mut brace_depth = 0
while cursor < source.length() {
let code = source.code_unit_at(cursor).to_int()
if code == 40 {
paren_depth += 1
} else if code == 41 {
paren_depth -= 1
} else if code == 91 {
bracket_depth += 1
} else if code == 93 {
bracket_depth -= 1
} else if code == 123 {
brace_depth += 1
} else if code == 125 {
brace_depth -= 1
}
if paren_depth == 0 && bracket_depth == 0 && brace_depth == 0 {
let next = if cursor + 1 < source.length() {
source.code_unit_at(cursor + 1).to_int()
} else {
-1
}
let after = if cursor + 2 < source.length() {
source.code_unit_at(cursor + 2).to_int()
} else {
-1
}
if code == 60 && next == 60 && after == 61 {
return Some(("<<=", cursor, cursor + 3))
}
if code == 62 && next == 62 && after == 61 {
return Some((">>=", cursor, cursor + 3))
}
if (
code == 43 ||
code == 45 ||
code == 42 ||
code == 47 ||
code == 37 ||
code == 38 ||
code == 124 ||
code == 94
) &&
next == 61 {
return Some((source[cursor:cursor + 2].to_owned(), cursor, cursor + 2))
}
if code == 61 {
return Some(("=", cursor, cursor + 1))
}
}
cursor += 1
}
None
}
///|
fn parser_parse_statement_expression(
source : String,
item_source : String,
) -> Expression raise WeslCompileError {
match @expression_parse.parse_expression_source(source) {
Parsed(node) => parser_expression_from_generated(node)
Failed(diagnostic) => {
if diagnostic.incomplete {
raise Parse(
"incomplete statement expression at byte \{diagnostic.offset}: \{item_source}",
)
}
let message = match diagnostic.message {
Some(message) => message
None => "invalid statement expression"
}
raise Parse("\{message} at byte \{diagnostic.offset}: \{item_source}")
}
}
}
///|
fn parser_parse_assignment_statement(
source : String,
item_source : String,
) -> AssignmentStatement raise WeslCompileError {
match parser_assignment_operator_at_top_level(source) {
Some((operator, start, end_)) =>
{
operator: parser_assignment_operator_from_text(operator),
lhs: parser_parse_statement_expression(
source[:start].trim().to_owned(),
item_source,
),
rhs: parser_parse_statement_expression(
source[end_:].trim().to_owned(),
item_source,
),
}
None => raise Parse("expected assignment operator in statement: \{source}")
}
}
///|
fn parser_statement_assignment(
statement : @statement_parse.StatementParseNode,
item_source : String,
) -> AssignmentStatement? raise WeslCompileError {
match statement.kind {
Assignment | CompoundAssignment => {
let source = parser_trim_statement_semicolon(
parser_strip_statement_attributes(statement.source),
)
Some(parser_parse_assignment_statement(source, item_source))
}
_ => None
}
}
///|
fn parser_statement_update_expression(
statement : @statement_parse.StatementParseNode,
item_source : String,
) -> Expression? raise WeslCompileError {
match statement.kind {
Increment | Decrement => {
let source = parser_trim_statement_semicolon(
parser_strip_statement_attributes(statement.source),
)
if source.length() < 2 {
raise Parse(
"expected update expression in statement: \{statement.source}",
)
}
Some(
parser_parse_statement_expression(
source[:source.length() - 2].trim().to_owned(),
item_source,
),
)
}
_ => None
}
}
///|
fn parser_skip_layout_and_comments(source : String, index : Int) -> Int {
let mut cursor = index
let mut running = true
while running && cursor < source.length() {
running = false
while cursor < source.length() {
let code = source.code_unit_at(cursor).to_int()
if code == 32 || code == 9 || code == 10 || code == 13 {
cursor += 1
running = true
} else {
break
}
}
if cursor + 1 < source.length() &&
source.code_unit_at(cursor).to_int() == 47 {
let next = source.code_unit_at(cursor + 1).to_int()
if next == 47 {
cursor += 2
while cursor < source.length() &&
source.code_unit_at(cursor).to_int() != 10 {
cursor += 1
}
running = true
} else if next == 42 {
cursor += 2
while cursor + 1 < source.length() {
if source.code_unit_at(cursor).to_int() == 42 &&
source.code_unit_at(cursor + 1).to_int() == 47 {
cursor += 2
break
}
cursor += 1
}
running = true
}
}
}
cursor
}
///|
fn parser_find_matching_delimiter(
source : String,
open_index : Int,
open_code : Int,
close_code : Int,
) -> Int? {
if open_index >= source.length() ||
source.code_unit_at(open_index).to_int() != open_code {
return None
}
let mut cursor = open_index + 1
let mut depth = 1
while cursor < source.length() {
let code = source.code_unit_at(cursor).to_int()
if code == 47 && cursor + 1 < source.length() {
let next = source.code_unit_at(cursor + 1).to_int()
if next == 47 {
cursor += 2
while cursor < source.length() &&
source.code_unit_at(cursor).to_int() != 10 {
cursor += 1
}
continue
}
if next == 42 {
cursor += 2
while cursor + 1 < source.length() {
if source.code_unit_at(cursor).to_int() == 42 &&
source.code_unit_at(cursor + 1).to_int() == 47 {
cursor += 2
break
}
cursor += 1
}
continue
}
}
if code == open_code {
depth += 1
} else if code == close_code {
depth -= 1
if depth == 0 {
return Some(cursor)
}
}
cursor += 1
}
None
}
///|
fn parser_parse_embedded_function_body(
body_source : String,
item_source : String,
) -> FunctionBody raise WeslCompileError {
match @statement_parse.parse_function_body_source(body_source) {
Parsed(node) =>
parser_function_body_from_generated(node.statements, item_source)
Failed(diagnostic) => {
if diagnostic.incomplete {
raise Parse(
"incomplete embedded function body at byte \{diagnostic.offset}: \{item_source}",
)
}
raise Parse(
"invalid embedded function body at byte \{diagnostic.offset}: \{item_source}",
)
}
}
}
///|
fn parser_parse_braced_statement_body(
source : String,
open_index : Int,
item_source : String,
) -> (FunctionBody, Int) raise WeslCompileError {
match parser_find_matching_delimiter(source, open_index, 123, 125) {
Some(close_index) =>
(
parser_parse_embedded_function_body(
source[open_index + 1:close_index].to_owned(),
item_source,
),
close_index,
)
None => raise Parse("expected statement body closing brace: \{item_source}")
}
}
///|
fn parser_parse_parenthesized_condition(
source : String,
open_index : Int,
item_source : String,
) -> (Expression, Int) raise WeslCompileError {
match parser_find_matching_delimiter(source, open_index, 40, 41) {
Some(close_index) =>
(
parser_parse_statement_expression(
source[open_index + 1:close_index].trim().to_owned(),
item_source,
),
close_index,
)
None =>
raise Parse(
"expected statement condition closing parenthesis: \{item_source}",
)
}
}
///|
fn parser_find_control_body_start(source : String, index : Int) -> Int? {
let mut cursor = index
let mut paren_depth = 0
let mut bracket_depth = 0
while cursor < source.length() {
let code = source.code_unit_at(cursor).to_int()
if code == 47 && cursor + 1 < source.length() {
let next = source.code_unit_at(cursor + 1).to_int()
if next == 47 {
cursor += 2
while cursor < source.length() &&
source.code_unit_at(cursor).to_int() != 10 {
cursor += 1
}
continue
}
if next == 42 {
cursor += 2
while cursor + 1 < source.length() {
if source.code_unit_at(cursor).to_int() == 42 &&
source.code_unit_at(cursor + 1).to_int() == 47 {
cursor += 2
break
}
cursor += 1
}
continue
}
}
if code == 40 {
paren_depth += 1
} else if code == 41 {
paren_depth -= 1
} else if code == 91 {
bracket_depth += 1
} else if code == 93 {
bracket_depth -= 1
} else if code == 123 && paren_depth == 0 && bracket_depth == 0 {
return Some(cursor)
}
cursor += 1
}
None
}
///|
fn parser_parse_control_condition_and_body_start(
source : String,
start_index : Int,
item_source : String,
) -> (Expression, Int) raise WeslCompileError {
let condition_start = parser_skip_layout_and_comments(source, start_index)
if condition_start < source.length() &&
source.code_unit_at(condition_start).to_int() == 40 {
let (condition, condition_end) = parser_parse_parenthesized_condition(
source, condition_start, item_source,
)
let body_start = parser_skip_layout_and_comments(source, condition_end + 1)
return (condition, body_start)
}
match parser_find_control_body_start(source, condition_start) {
Some(body_start) =>
(
parser_parse_statement_expression(
source[condition_start:body_start].trim().to_owned(),
item_source,
),
body_start,
)
None =>
raise Parse(
"expected statement control body opening brace: \{item_source}",
)
}
}
///|
fn parser_split_for_header(
source : String,
) -> Array[String] raise WeslCompileError {
let parts : Array[String] = []
let mut cursor = 0
let mut part_start = 0
let mut paren_depth = 0
let mut bracket_depth = 0
let mut brace_depth = 0
while cursor < source.length() {
let code = source.code_unit_at(cursor).to_int()
if code == 47 && cursor + 1 < source.length() {
let next = source.code_unit_at(cursor + 1).to_int()
if next == 47 {
cursor += 2
while cursor < source.length() &&
source.code_unit_at(cursor).to_int() != 10 {
cursor += 1
}
continue
}
if next == 42 {
cursor += 2
while cursor + 1 < source.length() {
if source.code_unit_at(cursor).to_int() == 42 &&
source.code_unit_at(cursor + 1).to_int() == 47 {
cursor += 2
break
}
cursor += 1
}
continue
}
}
if code == 40 {
paren_depth += 1
} else if code == 41 {
paren_depth -= 1
} else if code == 91 {
bracket_depth += 1
} else if code == 93 {
bracket_depth -= 1
} else if code == 123 {
brace_depth += 1
} else if code == 125 {
brace_depth -= 1
} else if code == 59 &&
paren_depth == 0 &&
bracket_depth == 0 &&
brace_depth == 0 {
parts.push(source[part_start:cursor].trim().to_owned())
part_start = cursor + 1
}
cursor += 1
}
parts.push(source[part_start:].trim().to_owned())
if parts.length() != 3 {
raise Parse("expected three for-loop header segments")
}
parts
}
///|
fn parser_for_initializer(
source : String,
item_source : String,
) -> ForInitializer? raise WeslCompileError {
let text = source.trim().to_owned()
if text == "" {
return None
}
let stripped = parser_strip_statement_attributes(text)
if stripped.has_prefix("const ") ||
stripped.has_prefix("let ") ||
stripped.has_prefix("var ") {
return Some(
Declaration(parser_parse_statement_declaration(stripped, item_source)),
)
}
match parser_assignment_operator_at_top_level(stripped) {
Some(_) =>
Some(Assignment(parser_parse_assignment_statement(stripped, item_source)))
None =>
Some(Expression(parser_parse_statement_expression(stripped, item_source)))
}
}
///|
fn parser_for_update(
source : String,
item_source : String,
) -> ForUpdate? raise WeslCompileError {
let text = parser_trim_statement_semicolon(
parser_strip_statement_attributes(source),
)
if text == "" {
return None
}
if text.has_suffix("++") {
return Some(
Increment(
parser_parse_statement_expression(
text[:text.length() - 2].trim().to_owned(),
item_source,
),
),
)
}
if text.has_suffix("--") {
return Some(
Decrement(
parser_parse_statement_expression(
text[:text.length() - 2].trim().to_owned(),
item_source,
),
),
)
}
match parser_assignment_operator_at_top_level(text) {
Some(_) =>
Some(Assignment(parser_parse_assignment_statement(text, item_source)))
None =>
Some(Expression(parser_parse_statement_expression(text, item_source)))
}
}
///|
fn parser_keyword_at(source : String, index : Int, keyword : String) -> Bool {
if index + keyword.length() > source.length() {
return false
}
if source[index:index + keyword.length()] != keyword {
return false
}
let before_ok = if index == 0 {
true
} else {
!parser_identifier_continues_before(source, index)
}
let after = index + keyword.length()
let after_ok = if after >= source.length() {
true
} else {
parser_identifier_continue_width(source, after) == 0
}
before_ok && after_ok
}
///|
fn parser_find_switch_clause_header_end(
source : String,
index : Int,
) -> (Int, Int)? {
let mut cursor = index
let mut paren_depth = 0
let mut bracket_depth = 0
while cursor < source.length() {
let code = source.code_unit_at(cursor).to_int()
if code == 47 && cursor + 1 < source.length() {
let next = source.code_unit_at(cursor + 1).to_int()
if next == 47 {
cursor += 2
while cursor < source.length() &&
source.code_unit_at(cursor).to_int() != 10 {
cursor += 1
}
continue
}
if next == 42 {
cursor += 2
while cursor + 1 < source.length() {
if source.code_unit_at(cursor).to_int() == 42 &&
source.code_unit_at(cursor + 1).to_int() == 47 {
cursor += 2
break
}
cursor += 1
}
continue
}
}
if code == 40 {
paren_depth += 1
} else if code == 41 {
paren_depth -= 1
} else if code == 91 {
bracket_depth += 1
} else if code == 93 {
bracket_depth -= 1
} else if paren_depth == 0 && bracket_depth == 0 {
if code == 58 {
return Some(
(cursor, parser_skip_layout_and_comments(source, cursor + 1)),
)
}
if code == 123 {
return Some((cursor, cursor))
}
}
cursor += 1
}
None
}
///|
fn parser_skip_switch_clause_attributes(source : String, index : Int) -> Int {
let mut cursor = parser_skip_layout_and_comments(source, index)
let mut running = true
while running && cursor < source.length() {
running = false
if source.code_unit_at(cursor).to_int() == 64 {
cursor += 1
let start_width = parser_identifier_start_width(source, cursor)
if start_width == 0 {
return index
}
cursor += start_width
while cursor < source.length() {
let width = parser_identifier_continue_width(source, cursor)
if width == 0 {
break
}
cursor += width
}
cursor = parser_skip_layout_and_comments(source, cursor)
if cursor < source.length() && source.code_unit_at(cursor).to_int() == 40 {
let (end_, ok) = parser_skip_balanced_attribute_arguments(
source,
cursor + 1,
)
if !ok {
return index
}
cursor = end_
}
cursor = parser_skip_layout_and_comments(source, cursor)
running = true
}
}
cursor
}
///|
fn parser_switch_clause_header_at(source : String, index : Int) -> Int? {
let header = parser_skip_switch_clause_attributes(source, index)
if parser_keyword_at(source, header, "case") ||
parser_keyword_at(source, header, "default") {
Some(header)
} else {
None
}
}
///|
fn parser_find_next_switch_clause(source : String, index : Int) -> Int {
let mut cursor = index
let mut paren_depth = 0
let mut bracket_depth = 0
let mut brace_depth = 0
while cursor < source.length() {
let code = source.code_unit_at(cursor).to_int()
if code == 47 && cursor + 1 < source.length() {
let next = source.code_unit_at(cursor + 1).to_int()
if next == 47 {
cursor += 2
while cursor < source.length() &&
source.code_unit_at(cursor).to_int() != 10 {
cursor += 1
}
continue
}
if next == 42 {
cursor += 2
while cursor + 1 < source.length() {
if source.code_unit_at(cursor).to_int() == 42 &&
source.code_unit_at(cursor + 1).to_int() == 47 {
cursor += 2
break
}
cursor += 1
}
continue
}
}
if code == 40 {
paren_depth += 1
} else if code == 41 {
paren_depth -= 1
} else if code == 91 {
bracket_depth += 1
} else if code == 93 {
bracket_depth -= 1
} else if code == 123 {
brace_depth += 1
} else if code == 125 {
brace_depth -= 1
}
if paren_depth == 0 && bracket_depth == 0 && brace_depth == 0 {
if parser_switch_clause_header_at(source, cursor) is Some(_) {
return cursor
}
}
cursor += 1
}
source.length()
}
///|
fn parser_split_top_level_commas(source : String) -> Array[String] {
let parts : Array[String] = []
let mut cursor = 0
let mut part_start = 0
let mut paren_depth = 0
let mut bracket_depth = 0
let mut brace_depth = 0
while cursor < source.length() {
let code = source.code_unit_at(cursor).to_int()
if code == 40 {
paren_depth += 1
} else if code == 41 {
paren_depth -= 1
} else if code == 91 {
bracket_depth += 1
} else if code == 93 {
bracket_depth -= 1
} else if code == 123 {
brace_depth += 1
} else if code == 125 {
brace_depth -= 1
} else if code == 44 &&
paren_depth == 0 &&
bracket_depth == 0 &&
brace_depth == 0 {
parts.push(source[part_start:cursor].trim().to_owned())
part_start = cursor + 1
}
cursor += 1
}
let tail = source[part_start:].trim().to_owned()
if tail != "" {
parts.push(tail)
}
parts
}
///|
fn parser_parse_switch_selectors(
source : String,
item_source : String,
) -> Array[Expression] raise WeslCompileError {
let selectors : Array[Expression] = []
for selector in parser_split_top_level_commas(source) {
selectors.push(parser_parse_statement_expression(selector, item_source))
}
selectors
}
///|
fn parser_parse_switch_case_body(
source : String,
item_source : String,
) -> FunctionBody raise WeslCompileError {
let trimmed = source.trim().to_owned()
let body_start = parser_skip_layout_and_comments(trimmed, 0)
if body_start < trimmed.length() &&
trimmed.code_unit_at(body_start).to_int() == 123 {
match parser_find_matching_delimiter(trimmed, body_start, 123, 125) {
Some(close_index) => {
let after_body = parser_skip_layout_and_comments(
trimmed,
close_index + 1,
)
if after_body == trimmed.length() {
return parser_parse_embedded_function_body(
trimmed[body_start + 1:close_index].to_owned(),
item_source,
)
}
}
None => ()
}
}
parser_parse_embedded_function_body(trimmed, item_source)
}
///|
fn parser_parse_switch_cases(
source : String,
item_source : String,
) -> Array[SwitchCase] raise WeslCompileError {
let cases : Array[SwitchCase] = []
let mut cursor = parser_skip_layout_and_comments(source, 0)
while cursor < source.length() {
let header = match parser_switch_clause_header_at(source, cursor) {
Some(header) => header
None =>
raise Parse("expected switch case or default clause: \{item_source}")
}
let is_default = parser_keyword_at(source, header, "default")
let keyword_len = if is_default {
7
} else if parser_keyword_at(source, header, "case") {
4
} else {
raise Parse("expected switch case or default clause: \{item_source}")
}
let header_start = parser_skip_layout_and_comments(
source,
header + keyword_len,
)
let (header_end, body_start) = match
parser_find_switch_clause_header_end(source, header_start) {
Some(result) => result
None => raise Parse("expected switch clause body: \{item_source}")
}
let selectors = if is_default {
[]
} else {
parser_parse_switch_selectors(
source[header_start:header_end].trim().to_owned(),
item_source,
)
}
let body_end = parser_find_next_switch_clause(source, body_start)
cases.push({
selectors,
is_default,
body: parser_parse_switch_case_body(
source[body_start:body_end].trim().to_owned(),
item_source,
),
attributes: parser_statement_attributes(source[cursor:header].to_owned()),
})
cursor = parser_skip_layout_and_comments(source, body_end)
}
cases
}
///|
fn parser_statement_block_control(
source : String,
item_source : String,
) -> ControlStatement raise WeslCompileError {
let body_start = parser_skip_layout_and_comments(source, 0)
let (body, _) = parser_parse_braced_statement_body(
source, body_start, item_source,
)
Block({ body, })
}
///|
fn parser_statement_loop_control(
source : String,
item_source : String,
) -> ControlStatement raise WeslCompileError {
let body_start = parser_skip_layout_and_comments(source, 4)
let (body, _) = parser_parse_braced_statement_body(
source, body_start, item_source,
)
Loop({ body, })
}
///|
fn parser_statement_for_control(
source : String,
item_source : String,
) -> ControlStatement raise WeslCompileError {
let header_start = parser_skip_layout_and_comments(source, 3)
match parser_find_matching_delimiter(source, header_start, 40, 41) {
Some(header_end) => {
let parts = parser_split_for_header(
source[header_start + 1:header_end].to_owned(),
)
let condition : Expression? = if parts[1] == "" {
None
} else {
Some(parser_parse_statement_expression(parts[1], item_source))
}
let body_start = parser_skip_layout_and_comments(source, header_end + 1)
let (body, _) = parser_parse_braced_statement_body(
source, body_start, item_source,
)
For({
initializer: parser_for_initializer(parts[0], item_source),
condition,
update: parser_for_update(parts[2], item_source),
body,
})
}
None =>
raise Parse(
"expected for-loop header closing parenthesis: \{item_source}",
)
}
}
///|
fn parser_statement_switch_control(
source : String,
item_source : String,
) -> ControlStatement raise WeslCompileError {
let (selector, body_start) = parser_parse_control_condition_and_body_start(
source, 6, item_source,
)
let body_end = match
parser_find_matching_delimiter(source, body_start, 123, 125) {
Some(body_end) => body_end
None => raise Parse("expected switch body closing brace: \{item_source}")
}
Switch({
selector,
cases: parser_parse_switch_cases(
source[body_start + 1:body_end].to_owned(),
item_source,
),
})
}
///|
fn parser_statement_continuing_control(
source : String,
item_source : String,
) -> ControlStatement raise WeslCompileError {
let body_start = parser_skip_layout_and_comments(source, 10)
let (body, _) = parser_parse_braced_statement_body(
source, body_start, item_source,
)
Continuing({ body, })
}
///|
fn parser_statement_while_control(
source : String,
item_source : String,
) -> ControlStatement raise WeslCompileError {
let (condition, body_start) = parser_parse_control_condition_and_body_start(
source, 5, item_source,
)
let (body, _) = parser_parse_braced_statement_body(
source, body_start, item_source,
)
While({ condition, body })
}
///|
fn parser_statement_if_control(
source : String,
item_source : String,
) -> ControlStatement raise WeslCompileError {
let (condition, body_start) = parser_parse_control_condition_and_body_start(
source, 2, item_source,
)
let (body, body_end) = parser_parse_braced_statement_body(
source, body_start, item_source,
)
let after_body = parser_skip_layout_and_comments(source, body_end + 1)
let else_body = if source[after_body:].trim().has_prefix("else") {
let else_start = parser_skip_layout_and_comments(source, after_body + 4)
if else_start < source.length() &&
source.code_unit_at(else_start).to_int() == 123 {
let (body, _) = parser_parse_braced_statement_body(
source, else_start, item_source,
)
Some(body)
} else {
Some(
parser_parse_embedded_function_body(
source[else_start:].trim().to_owned(),
item_source,
),
)
}
} else {
None
}
If({ condition, body, else_body })
}
///|
fn parser_statement_control(
statement : @statement_parse.StatementParseNode,
item_source : String,
) -> ControlStatement? raise WeslCompileError {
let source = parser_strip_statement_attributes(statement.source)
match statement.kind {
Block => Some(parser_statement_block_control(source, item_source))
If => Some(parser_statement_if_control(source, item_source))
Switch => Some(parser_statement_switch_control(source, item_source))
Loop => Some(parser_statement_loop_control(source, item_source))
For => Some(parser_statement_for_control(source, item_source))
While => Some(parser_statement_while_control(source, item_source))
Continuing => Some(parser_statement_continuing_control(source, item_source))
_ => None
}
}
///|
fn parser_statement_attributes(
source : String,
) -> Array[Attribute] raise WeslCompileError {
match @attribute_parse.parse_attributes_source(source) {
Parsed(success) => {
let attrs : Array[Attribute] = []
for attr in success.attributes {
attrs.push({
name: attr.name,
arguments: attr.arguments,
argument_exprs: parser_parse_attribute_argument_exprs(
attr.name,
attr.arguments,
source,
),
condition_expr: parser_parse_attribute_condition_expr(
attr.name,
attr.arguments,
),
span: { start_line: attr.start_line, end_line: attr.end_line },
})
}
attrs
}
Failed(diagnostic) => {
if diagnostic.incomplete {
raise Parse(
"incomplete statement attribute list at byte \{diagnostic.offset}",
)
}
raise Parse(
"invalid statement attribute list at byte \{diagnostic.offset}",
)
}
}
}
///|
fn parser_trim_statement_semicolon(source : String) -> String {
let trimmed = source.trim().to_owned()
if trimmed.has_suffix(";") {
trimmed[:trimmed.length() - 1].trim().to_owned()
} else {
trimmed
}
}
///|
fn parser_skip_balanced_attribute_arguments(
source : String,
index : Int,
) -> (Int, Bool) {
let mut cursor = index
let mut depth = 1
while cursor < source.length() {
let code = source.code_unit_at(cursor).to_int()
if code == 40 {
depth += 1
} else if code == 41 {
depth -= 1
if depth == 0 {
return (cursor + 1, true)
}
}
cursor += 1
}
(cursor, false)
}
///|
fn parser_strip_statement_attributes(source : String) -> String {
let mut cursor = 0
while cursor < source.length() {
while cursor < source.length() {
let code = source.code_unit_at(cursor).to_int()
if code == 32 || code == 9 || code == 10 || code == 13 {
cursor += 1
} else {
break
}
}
if cursor >= source.length() || source.code_unit_at(cursor).to_int() != 64 {
break
}
cursor += 1
let start_width = parser_identifier_start_width(source, cursor)
if start_width == 0 {
break
}
cursor += start_width
while cursor < source.length() {
let width = parser_identifier_continue_width(source, cursor)
if width == 0 {
break
}
cursor += width
}
while cursor < source.length() {
let code = source.code_unit_at(cursor).to_int()
if code == 32 || code == 9 || code == 10 || code == 13 {
cursor += 1
} else {
break
}
}
if cursor < source.length() && source.code_unit_at(cursor).to_int() == 40 {
let (end_, ok) = parser_skip_balanced_attribute_arguments(
source,
cursor + 1,
)
if !ok {
break
}
cursor = end_
}
}
source[cursor:].trim().to_owned()
}
///|
fn parser_statement_expression(
statement : @statement_parse.StatementParseNode,
item_source : String,
) -> Expression? raise WeslCompileError {
let source = parser_trim_statement_semicolon(
parser_strip_statement_attributes(statement.source),
)
match statement.kind {
Return => {
let rest = source[6:].trim().to_owned()
if rest == "" {
None
} else {
Some(parser_parse_statement_expression(rest, item_source))
}
}
ConstAssert =>
Some(
parser_parse_statement_expression(
source[12:].trim().to_owned(),
item_source,
),
)
BreakIf =>
Some(
parser_parse_statement_expression(
source[8:].trim().to_owned(),
item_source,
),
)
Call => Some(parser_parse_statement_expression(source, item_source))
_ => None
}
}
///|
fn parser_parse_statement_declaration(
source : String,
item_source : String,
) -> StatementDeclaration raise WeslCompileError {
let binding = match @binding_parse.parse_binding_source(source) {
Parsed(node) => node
Failed(diagnostic) => {
if diagnostic.incomplete {
raise Parse(
"incomplete statement declaration at byte \{diagnostic.offset}: \{item_source}",
)
}
raise Parse(
"invalid statement declaration at byte \{diagnostic.offset}: \{item_source}",
)
}
}
{
kind: parser_statement_declaration_kind_from_binding(binding.kind),
name: binding.name,
template_arguments: binding.var_template,
type_text: binding.type_text,
type_expr: parser_parse_optional_type_expression(binding.type_text),
initializer: binding.initializer,
initializer_expr: match binding.initializer {
Some(expr) => Some(parser_parse_statement_expression(expr, item_source))
None => None
},
}
}
///|
fn parser_statement_declaration(
statement : @statement_parse.StatementParseNode,
item_source : String,
) -> StatementDeclaration? raise WeslCompileError {
match statement.kind {
Const | Let | Var =>
Some(
parser_parse_statement_declaration(
parser_strip_statement_attributes(statement.source),
item_source,
),
)
_ => None
}
}
///|
fn parser_function_body_from_generated(
statements : Array[@statement_parse.StatementParseNode],
item_source : String,
) -> FunctionBody raise WeslCompileError {
let mapped : Array[Statement] = []
for statement in statements {
mapped.push({
kind: parser_statement_kind_from_generated(statement.kind),
source: statement.source,
expression: parser_statement_expression(statement, item_source),
declaration: parser_statement_declaration(statement, item_source),
assignment: parser_statement_assignment(statement, item_source),
update_expression: parser_statement_update_expression(
statement, item_source,
),
control: parser_statement_control(statement, item_source),
attributes: parser_statement_attributes(statement.source),
})
}
{ statements: mapped }
}
///|
fn Parser::parse_function_body_with_yacc(
self : Parser,
body_source : String,
item_source : String,
) -> FunctionBody raise WeslCompileError {
match @statement_parse.parse_function_body_source(body_source) {
Parsed(node) =>
parser_function_body_from_generated(node.statements, item_source)
Failed(diagnostic) => {
let byte_offset = if self.tokens.length() > 0 {
self.tokens[0].start() + diagnostic.offset
} else {
diagnostic.offset
}
if diagnostic.incomplete {
raise Parse(
"incomplete function body at byte \{byte_offset}: \{item_source}",
)
}
raise Parse(
"invalid function body at byte \{byte_offset}: \{item_source}",
)
}
}
}
///|
fn Parser::parse_binding_with_yacc(
self : Parser,
item_source : String,
) -> @binding_parse.BindingParseNode raise WeslCompileError {
match @binding_parse.parse_binding_source(item_source) {
Parsed(node) => node
Failed(diagnostic) => {
let byte_offset = if self.tokens.length() > 0 {
self.tokens[0].start() + diagnostic.offset
} else {
diagnostic.offset
}
if diagnostic.incomplete {
raise Parse(
"incomplete binding declaration at byte \{byte_offset}: \{item_source}",
)
}
raise Parse(
"invalid binding declaration at byte \{byte_offset}: \{item_source}",
)
}
}
}
///|
fn Parser::parse_directive_with_yacc(
self : Parser,
item_source : String,
) -> @directive_parse.DirectiveParseNode raise WeslCompileError {
match @directive_parse.parse_directive_source(item_source) {
Parsed(node) => node
Failed(diagnostic) => {
let byte_offset = if self.tokens.length() > 0 {
self.tokens[0].start() + diagnostic.offset
} else {
diagnostic.offset
}
if diagnostic.incomplete {
raise Parse(
"incomplete directive at byte \{byte_offset}: \{item_source}",
)
}
raise Parse("invalid directive at byte \{byte_offset}: \{item_source}")
}
}
}
///|
fn Parser::parse_import_tree_with_yacc(
self : Parser,
context : String,
item_source : String,
) -> Array[ImportNode] raise WeslCompileError {
let first_start = match self.view() {
[token, ..] => token.start()
[] =>
raise Parse("unsupported import statement (\{context}): \{item_source}")
}
let tree_end = match self.tokens.last() {
Some(Semi(start, _, _)) => start
_ =>
raise Parse("unsupported import statement (\{context}): \{item_source}")
}
let tree_source = self.source_slice(first_start, tree_end)
let parsed = @import_parse.parse_import_tree_source(tree_source)
let generated_items = match parsed {
Parsed(nodes) => nodes
Failed(diagnostic) => {
let byte_offset = first_start + diagnostic.offset
if diagnostic.incomplete {
raise Parse(
"incomplete import statement at byte \{byte_offset} (\{context}): \{item_source}",
)
}
raise Parse(
"invalid import statement at byte \{byte_offset} (\{context}): \{item_source}",
)
}
}
let items : Array[ImportNode] = []
for item in generated_items {
items.push(parser_import_node_from_generated(item))
}
while true {
match self.view() {
[Semi(_, _, _), ..] => break
[_, .. rest] => self.update_view(rest)
[] =>
raise Parse("unsupported import statement (\{context}): \{item_source}")
}
}
items
}
///|
fn Parser::parse_import_statement(
self : Parser,
current_path : ModulePath,
) -> ImportStatement raise WeslCompileError {
let item_source = self.item_source().trim().to_owned()
let span = self.item_span()
let context = span.context(current_path)
guard self.tokens.last() is Some(Semi(_, _, _)) else {
raise Parse("unsupported import statement (\{context}): \{item_source}")
}
let attrs = self.parse_attributes() catch {
Parse(reason) => raise Parse("\{reason} (\{context})")
err => raise err
}
guard self.view() is [Ident("import", _, _, _), .. after_import] else {
raise Parse("unsupported import statement (\{context}): \{item_source}")
}
self.update_view(after_import)
let items = self.parse_import_tree_with_yacc(context, item_source)
if items.is_empty() {
raise Parse("unsupported import statement (\{context}): \{item_source}")
}
let statement = ImportStatement::{
span,
attributes: attrs,
entries: items,
source: item_source,
}
ignore(statement.flatten_items(current_path))
statement
}
///|
fn Parser::parse_global_declaration(
self : Parser,
) -> GlobalDeclaration raise WeslCompileError {
let span = self.item_span()
let item_source = self.item_source().trim().to_owned()
let attrs = self.parse_attributes()
let parsed = @global_parse.parse_global_header_source(item_source)
let header = match parsed {
Parsed(Function(name)) => {
parser_validate_declaration_ident(name)
let function_node = match
@function_parse.parse_function_source(item_source) {
Parsed(node) => node
Failed(diagnostic) => {
let byte_offset = if self.tokens.length() > 0 {
self.tokens[0].start() + diagnostic.offset
} else {
diagnostic.offset
}
if diagnostic.incomplete {
raise Parse(
"incomplete function declaration at byte \{byte_offset}: \{item_source}",
)
}
raise Parse(
"invalid function declaration at byte \{byte_offset}: \{item_source}",
)
}
}
GlobalDeclarationHeader::Function({
name,
generic_parameters: function_node.generic_parameters,
parameters: parser_function_parameters_from_generated(
function_node.parameters,
),
return_type: function_node.return_type,
return_type_expr: parser_parse_optional_type_expression(
function_node.return_type,
),
return_attributes: parser_function_return_attributes(item_source),
body: self.parse_function_body_with_yacc(
function_node.body_text,
item_source,
),
})
}
Parsed(Struct(name)) => {
parser_validate_declaration_ident(name)
let struct_ = match @parser.parse_wgsl_struct(item_source) {
Some(struct_) => struct_
None => raise Parse("invalid struct declaration: \{item_source}")
}
let members = parser_struct_members_from_wgsl(
item_source,
struct_.members(),
)
Struct({ name, members })
}
Parsed(Alias(name)) => {
parser_validate_declaration_ident(name)
let binding = self.parse_binding_with_yacc(item_source)
Alias({
name,
target: binding.type_text,
target_type: parser_parse_optional_type_expression(binding.type_text),
})
}
Parsed(Const(name)) => {
parser_validate_declaration_ident(name)
let binding = self.parse_binding_with_yacc(item_source)
Const({
name,
type_text: binding.type_text,
type_expr: parser_parse_optional_type_expression(binding.type_text),
initializer: binding.initializer,
initializer_expr: parser_parse_optional_initializer_expression(
binding.initializer,
item_source,
),
})
}
Parsed(Override(name)) => {
parser_validate_declaration_ident(name)
let binding = self.parse_binding_with_yacc(item_source)
Override({
name,
type_text: binding.type_text,
type_expr: parser_parse_optional_type_expression(binding.type_text),
initializer: binding.initializer,
initializer_expr: parser_parse_optional_initializer_expression(
binding.initializer,
item_source,
),
})
}
Parsed(Let(name)) => {
parser_validate_declaration_ident(name)
let binding = self.parse_binding_with_yacc(item_source)
Let({
name,
type_text: binding.type_text,
type_expr: parser_parse_optional_type_expression(binding.type_text),
initializer: binding.initializer,
initializer_expr: parser_parse_optional_initializer_expression(
binding.initializer,
item_source,
),
})
}
Parsed(Var(var_header)) => {
match var_header.name {
Some(name) => parser_validate_declaration_ident(name)
None => ()
}
let binding = self.parse_binding_with_yacc(item_source)
Var({
name: var_header.name,
template_arguments: binding.var_template,
type_text: binding.type_text,
type_expr: parser_parse_optional_type_expression(binding.type_text),
initializer: binding.initializer,
initializer_expr: parser_parse_optional_initializer_expression(
binding.initializer,
item_source,
),
})
}
Parsed(ConstAssert) =>
ConstAssert(parser_parse_const_assert_declaration(item_source))
Parsed(EnableDirective) => {
let directive = self.parse_directive_with_yacc(item_source)
EnableDirective({ names: directive.arguments })
}
Parsed(RequiresDirective) => {
let directive = self.parse_directive_with_yacc(item_source)
RequiresDirective({ names: directive.arguments })
}
Parsed(DiagnosticDirective) => {
let directive = self.parse_directive_with_yacc(item_source)
DiagnosticDirective(
parser_diagnostic_directive_from_arguments(directive.arguments),
)
}
Parsed(Other) => Other
Failed(diagnostic) => {
let byte_offset = if self.tokens.length() > 0 {
self.tokens[0].start() + diagnostic.offset
} else {
diagnostic.offset
}
if diagnostic.incomplete {
raise Parse(
"incomplete global declaration at byte \{byte_offset}: \{item_source}",
)
}
raise Parse(
"invalid global declaration at byte \{byte_offset}: \{item_source}",
)
}
}
{ header, attributes: attrs, source: item_source, span }
}
///|
fn parser_top_level_item_parsers(
source : String,
) -> Array[Parser] raise WeslCompileError {
let root_parser = Parser::new(source)
let spans = match @item_parse.parse_top_level_items_source(source) {
Parsed(spans) => spans
Failed(diagnostic) => {
if diagnostic.incomplete {
raise Parse(
"unexpected end of top-level item at byte \{diagnostic.offset}",
)
}
raise Parse("invalid top-level item at byte \{diagnostic.offset}")
}
}
let parsers : Array[Parser] = []
for span in spans {
let tokens : Array[ParserToken] = []
for token in root_parser.tokens {
if token.start() >= span.start_offset && token.end() <= span.end_offset {
tokens.push(token)
}
}
if !tokens.is_empty() {
parsers.push(Parser::from_tokens(source, tokens))
}
}
parsers
}
///|
pub fn parse_global_declarations(
source : String,
) -> Array[GlobalDeclaration] raise WeslCompileError {
let declarations : Array[GlobalDeclaration] = []
for item_parser in parser_top_level_item_parsers(source) {
if item_parser.item_is_import() {
continue
}
declarations.push(item_parser.parse_global_declaration())
}
declarations
}
///|
pub fn parse_translation_unit(
current_path : ModulePath,
source : String,
parse_imports : Bool,
) -> TranslationUnit raise WeslCompileError {
let imports : Array[ImportStatement] = []
let global_declarations : Array[GlobalDeclaration] = []
for item_parser in parser_top_level_item_parsers(source) {
if item_parser.item_is_import() {
if parse_imports {
imports.push(item_parser.parse_import_statement(current_path))
}
continue
}
global_declarations.push(item_parser.parse_global_declaration())
}
{ imports, global_declarations }
}