Created
October 2, 2020 01:08
-
-
Save AndrewRayCode/099eff7d4a89edc5f1be7e8ced30d874 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
// Map containing the names of structs defined in the shader mapped to "true". | |
var typeNames = { }; | |
// Identifer for each node. | |
var next_id = 0; | |
function pos() { | |
return { | |
line: 1, | |
column: 1, | |
offset: 1, | |
span: text().length | |
}; | |
} | |
/** @constructor */ | |
function node(extraProperties, position) { | |
return {type: extraProperties.type, pos: pos(), extraProperties}; | |
}; | |
// Helper function to daisy chain together a series of binary operations. | |
function daisy_chain(head, tail) { | |
var result = head; | |
for (var i = 0; i < tail.length; i++) { | |
result = new node({ | |
type: "binary", | |
operator: tail[i][1], | |
left: result, | |
right: tail[i][3] | |
}); | |
} | |
return result; | |
}; | |
} | |
start | |
= external_statement_list | |
newLine | |
= [\n] { | |
return "\n"; | |
} | |
EOF | |
= !. | |
_ "whitespace" | |
= (newLine / [\\\n] / [\r\t\f\v ] / comment)+ | |
noNewlineComment | |
= "/*" (!"*/" .)* "*/" | |
/ "//" [^\n]* | |
noNewlineWhitespace | |
= ([\r\t\f\v ] / noNewlineComment)+ | |
comment "comment" | |
= "/*" (!"*/" .)* "*/" | |
/ "//" [^\n]* (newLine / EOF) | |
semicolon = _? ";" _? | |
comma = _? "," _? | |
left_bracket = _? "[" _? | |
right_bracket = _? "]" _? | |
equals = _? "=" _? | |
left_paren = _? "(" _? | |
right_paren = _? ")" _? | |
left_brace = _? "{" _? | |
right_brace = _? "}" _? | |
external_statement_list | |
= statements:external_statement* { | |
// Skip blank statements. These were either whitespace or | |
var result = new node({ | |
type: "root", | |
statements: [] | |
}); | |
for (var i = 0; i < statements.length; i++) { | |
if (statements[i]) { | |
result.statements = result.statements.concat(statements[i]); | |
} | |
} | |
return result; | |
} | |
external_statement | |
= statement:(external_declaration) { return statement; } | |
/ _ { return ""; } | |
external_declaration | |
= function_definition | |
/ global_declaration | |
/ struct_definition | |
/ macro_call | |
macro_identifier | |
= head:[A-Za-z_] tail:[A-Za-z_0-9]* { | |
return new node({ | |
type: "identifier", | |
name: head + tail.join("") | |
}); | |
} | |
macro_paren_parameter = | |
left_paren value:(head:[^()]* paren:macro_paren_parameter? tail:[^()]* { | |
return head.join("") + paren + tail.join(""); | |
}) right_paren { | |
return "(" + value + ")"; | |
} | |
macro_call_parameter = | |
macro_paren_parameter / value:[^,)]* { | |
return value.join(""); | |
} | |
macro_call_parameter_list = | |
head:macro_call_parameter tail:(comma macro_call_parameter)* { | |
return [head].concat(tail.map(function(item) { return item[1]; })); | |
} | |
macro_call | |
= macro_name:macro_identifier _? left_paren | |
// Explicitly use "")" at the end of the line as to not eat any whitespace | |
// after the macro call. | |
parameters:(parameter_list?) ")" { | |
var result = new node({ | |
type: "macro_call", | |
macro_name: macro_name, | |
parameters: parameters | |
}); | |
if (!parameters) { | |
result.parameters = []; | |
} | |
return result; | |
} | |
macro_call_line = | |
head:macro_call? tail:[^\n]* { | |
return { | |
macro_call: head, | |
rest_of_line: tail.join('') | |
} | |
} | |
function_definition | |
= prototype:function_prototype body:compound_statement { | |
result = new node({ | |
type: "function_declaration", | |
name: prototype.name, | |
returnType: prototype.returnType, | |
parameters: prototype.parameters, | |
body: body | |
}); | |
return result; | |
} | |
compound_statement | |
= left_brace statements:statement_list? right_brace { | |
result = new node({ | |
type: "scope", | |
statements: [] | |
}); | |
if (statements && statements.statements) { | |
result.statements = statements.statements; | |
} | |
return result; | |
} | |
statement_list | |
= _? list:(statement_no_new_scope)* _? {return {statements: list};} | |
statement_no_new_scope | |
= compound_statement | |
/ simple_statement | |
statement_with_scope | |
= compound_statement | |
/ simple_statement | |
simple_statement | |
= statement:(declaration_statement | |
/ expression_statement | |
/ selection_statement | |
/ iteration_statement | |
/ jump_statement | |
/ macro_call) { | |
return statement; | |
} | |
declaration_statement | |
= declaration | |
selection_statement | |
= "if" left_paren condition:expression right_paren | |
if_body:statement_with_scope | |
else_body:("else" (_)? statement_with_scope)? { | |
result = new node({ | |
type:"if_statement", | |
condition:condition, | |
body:if_body | |
}); | |
if (else_body) { | |
result.elseBody = else_body[2]; | |
} | |
return result; | |
} | |
for_loop | |
= "for" left_paren | |
initializer:(expression_statement / declaration_statement) | |
condition:condition? semicolon | |
increment:expression? right_paren | |
body:statement_no_new_scope { | |
return new node({ | |
type:"for_statement", | |
initializer:initializer, | |
condition:condition, | |
increment:increment, | |
body:body | |
}); | |
} | |
while_statement | |
= "while" left_paren condition:condition right_paren { | |
return { | |
condition:condition | |
}; | |
} | |
while_loop | |
= w:while_statement body:statement_no_new_scope { | |
return new node({ | |
type: "while_statement", | |
condition: w.condition, | |
body: body | |
}); | |
} | |
do_while | |
= "do" body:statement_with_scope w:while_statement { | |
return new node({ | |
type: "do_statement", | |
condition: w.condition, | |
body: body | |
}); | |
} | |
iteration_statement | |
= while_loop | |
/ do_while | |
/ for_loop | |
jump_statement | |
= "return" expression:expression semicolon { | |
return new node({ | |
type: "return", | |
value: expression | |
}); | |
} | |
/ type:("continue" semicolon | |
/ "break" semicolon | |
/ "return" semicolon | |
/ "discard" semicolon) { | |
return new node({ | |
type:type[0] | |
}); | |
} | |
expression_statement | |
= e:expression? semicolon { | |
return new node({ | |
type: "expression", | |
expression: e | |
}); | |
} | |
declaration "declaration" | |
= function_prototype:function_prototype semicolon { | |
return function_prototype; | |
} | |
/ type:locally_specified_type _ declarators:init_declarator_list semicolon { | |
return new node({ | |
type: "declarator", | |
typeAttribute: type, | |
declarators: declarators | |
}); | |
} | |
/ "invariant" _ head:identifier tail:(comma identifier)* semicolon { | |
var items = [ head ].concat(tail.map(function(item) { | |
return item[1]; })); | |
return new node({ | |
type: "invariant", | |
identifiers: items | |
}); | |
} | |
/ "precision" _ precission:precision_qualifier _ type:type_name semicolon { | |
return new node({ | |
type:"precision", | |
precision: precission, | |
typeName: type | |
}); | |
} | |
global_declaration | |
= declaration | |
/ type:fully_specified_type _ declarators:init_declarator_list semicolon { | |
return new node({ | |
type: "declarator", | |
typeAttribute: type, | |
declarators: declarators | |
}); | |
} | |
/ type:attribute_type _ declarators:declarator_list_no_array semicolon { | |
return new node({ | |
type: "declarator", | |
typeAttribute: type, | |
declarators: declarators | |
}); | |
} | |
function_prototype_parameter_list | |
= "void" / | |
head:parameter_declaration | |
tail:(comma parameter_declaration)* { | |
return [ head ].concat(tail.map(function(item) { return item[1]; })); | |
} | |
function_prototype | |
= type:(void_type/precision_type) _ | |
identifier:identifier left_paren | |
parameters:function_prototype_parameter_list? right_paren { | |
result = new node({ | |
type:"function_prototype", | |
name: identifier.name, | |
returnType: type, | |
parameters: parameters | |
}); | |
if (parameters == "void" || !parameters) { | |
result.parameters = []; | |
} | |
return result; | |
} | |
parameter_qualifier | |
= "inout" / "in" / "out" | |
parameter_declaration | |
= const_qualifier:(const_qualifier _)? | |
parameter:(parameter_qualifier _)? | |
precision:(precision_qualifier _)? | |
type_name:type_name _ | |
identifier:identifier array_size:(left_bracket | |
constant_expression | |
right_bracket)? | |
{ | |
var result = new node({ | |
type: "parameter", | |
type_name: type_name, | |
name: identifier.name | |
}); | |
if (const_qualifier) result.typeQualifier = const_qualifier[0]; | |
if (parameter) result.parameterQualifier = parameter[0]; | |
if (precision) result.precision = precision[0]; | |
if (array_size) result.arraySize = array_size[1]; | |
// "const" is only legal on "in" parameter qualifiers. | |
if (result.typeQualifier && | |
result.parameterQualifier && | |
result.parameterQualifier != "in") { | |
return null; | |
} else { | |
return result; | |
} | |
} | |
init_declarator_list | |
= head:init_declarator tail:(comma init_declarator)* { | |
return [ head ].concat(tail.map(function(item) { return item[1]; })); | |
} | |
declarator_list | |
= head:declarator tail:(comma declarator)* { | |
return [ head ].concat(tail.map(function(item) { return item[1]; })); | |
} | |
declarator_list_no_array | |
= head:declarator_no_array tail:(comma declarator_no_array)* { | |
return [ head ].concat(tail.map(function(item) { return item[1]; })); | |
} | |
declarator_list_arrays_have_size | |
= head:declarator_array_with_size tail:(comma declarator_array_with_size)* { | |
return [ head ].concat(tail.map(function(item) { return item[1]; })); | |
} | |
declarator_no_array | |
= name:identifier { | |
return new node({ | |
type: "declarator_item", | |
name:name | |
}); | |
} | |
declarator_array_with_size | |
= name:identifier left_bracket arraySize:constant_expression right_bracket { | |
return new node({ | |
type: "declarator_item", | |
name: name, | |
arraySize: arraySize, | |
isArray: true | |
}); | |
} | |
/ declarator_no_array | |
declarator | |
= name:identifier left_bracket right_bracket { | |
return new node({ | |
type: "declarator_item", | |
name: name, | |
isArray: true | |
}); | |
} | |
/ declarator_array_with_size | |
init_declarator | |
= name:identifier equals initializer:constant_expression { | |
return new node({ | |
type: "declarator_item", | |
name: name, | |
initializer:initializer | |
}); | |
} | |
/ declarator | |
member_list | |
= declarators:(locally_specified_type _ | |
declarator_list_arrays_have_size | |
semicolon)+ { | |
return declarators.map(function(item) { | |
return new node({ | |
type: "declarator", | |
typeAttribute: item[0], | |
declarators: item[2] | |
}) | |
}); | |
} | |
struct_definition | |
= qualifier:((type_qualifier/attribute_qualifier) _)? "struct" | |
identifier:(_ identifier)? left_brace | |
members:member_list | |
right_brace declarators:declarator_list? semicolon { | |
var result = new node({ | |
type: "struct_definition", | |
members:members | |
}); | |
if (qualifier) { | |
result.qualifier = qualifier[0]; | |
} | |
if (identifier) { | |
result.name = identifier[1].name; | |
typeNames[result.name] = result; | |
} | |
if (declarators) { | |
result.declarators = declarators; | |
} | |
return result; | |
} | |
constant_expression | |
= conditional_expression | |
precision_type | |
= precision:(precision_qualifier _)? name:type_name { | |
var result = new node({ | |
type: "type", | |
name: name | |
}); | |
if (precision) result.precision = precision[0]; | |
return result; | |
} | |
locally_specified_type "locally specified type" | |
= qualifier:(const_qualifier _)? type:precision_type { | |
var result = type; | |
if (qualifier) result.qualifier = qualifier[0]; | |
return result; | |
} | |
attribute_qualifier | |
= "attribute" { | |
return "attribute"; | |
} | |
attribute_type "locally specified type" | |
= qualifier:attribute_qualifier _ type:precision_type { | |
var result = type; | |
result.qualifier = qualifier; | |
return result; | |
} | |
fully_specified_type "fully specified type" | |
= qualifier:(type_qualifier _)? type:precision_type { | |
var result = type; | |
if (qualifier) result.qualifier = qualifier[0]; | |
return result; | |
} | |
precision_qualifier "precision qualifier" | |
= "highp" / "mediump" / "lowp" | |
const_qualifier | |
= "const" | |
type_qualifier "type qualifier" | |
= const_qualifier | |
/ "varying" | |
/ "invariant" _ "varying" { return "invariant varying"; } | |
/ "uniform" | |
void_type "void" | |
= "void" { | |
return new node({ | |
type: "type", | |
name: "void" | |
}) | |
} | |
type_name "type name" | |
= "float" | |
/ "int" | |
/ "bool" | |
/ "sampler2D" | |
/ "samplerCube" | |
/ vector | |
/ matrix | |
/ name:identifier { | |
if (name.name in typeNames) { | |
return name.name; | |
} else { | |
return null; | |
} | |
} | |
identifier "identifier" | |
= !(keyword [^A-Za-z_0-9]) head:[A-Za-z_] tail:[A-Za-z_0-9]* { | |
return new node({ | |
type: "identifier", | |
name: head + tail.join("") | |
}); | |
} | |
keyword "keyword" | |
= "attribute" / "const" / "bool" / "float" / "int" | |
/ "break" / "continue" / "do" / "else" / "for" / "if" | |
/ "discard" / "return" / vector / matrix | |
/ "in" / "out" / "inout" / "uniform" / "varying" | |
/ "sampler2D" / "samplerCube" / "struct" / "void" | |
/ "while" / "highp" / "mediump" / "lowp" / "true" / "false" | |
vector = a:([bi]? "vec" [234]) { return a.join(""); } | |
matrix = a:("mat" [234]) { return a.join(""); } | |
reserved "reserved name" | |
= single_underscore_identifier* "__" [A-Za-z_0-9]* | |
single_underscore_identifier | |
= [A-Za-z0-9]* "_" [A-Za-z0-9]+ | |
int_constant | |
= head:[1-9] tail:[0-9]* { | |
return new node({ | |
type: "int", | |
value: parseInt([head].concat(tail).join(""), 10) | |
}); | |
} | |
/ "0"[Xx] digits:[0-9A-Fa-f]+ { | |
return new node({ | |
type:"int", | |
value:parseInt(digits.join(""), 16) | |
}); | |
} | |
/ "0" digits:[0-7]+ { | |
return new node({ | |
type:"int", | |
value:parseInt(digits.join(""), 8) | |
}); | |
} | |
/ "0" { | |
return new node({ | |
type: "int", | |
value: 0 | |
}); | |
} | |
float_constant | |
= digits:([0-9]*"."[0-9]+float_exponent? / [0-9]+"."[0-9]*float_exponent?) | |
{ | |
digits[0] = digits[0].join(""); | |
digits[2] = digits[2].join(""); | |
return new node({ | |
type: "float", | |
value:parseFloat(digits.join("")) | |
}); | |
} | |
/ digits:([0-9]+float_exponent) { | |
return new node({ | |
type: "float", | |
value: parseFloat(digits[0].join("") + digits[1]) | |
}); | |
} | |
float_exponent | |
= [Ee] sign:[+-]? exponent:[0-9]+ { | |
return ["e", sign].concat(exponent).join(""); | |
} | |
paren_expression | |
= left_paren expression:expression right_paren { | |
return expression; | |
} | |
bool_constant | |
= value:("true" / "false") { | |
return new node({ | |
type: "bool", | |
value: value == "true" | |
}); | |
} | |
primary_expression | |
= function_call | |
/ identifier | |
/ float_constant | |
/ int_constant | |
/ bool_constant | |
/ paren_expression | |
index_accessor | |
= left_bracket index:expression right_bracket { | |
return new node({ | |
type: "accessor", | |
index: index | |
}); | |
} | |
field_selector | |
= "." id:identifier { | |
return new node({ | |
type: "field_selector", | |
selection: id.name | |
}) | |
} | |
postfix_expression | |
= head:primary_expression | |
tail:(field_selector / index_accessor)* | |
{ | |
var result = head; | |
for (var i = 0; i < tail.length; i++) { | |
result = new node({ | |
type: "postfix", | |
operator: tail[i], | |
expression: result | |
}) | |
} | |
return result; | |
} | |
postfix_expression_no_repeat | |
= head:postfix_expression _? | |
tail:("++" / "--")? | |
rest:(field_selector / index_accessor)* { | |
var result = head; | |
if(tail) { | |
result = new node({ | |
type: "postfix", | |
operator: new node({ | |
id: next_id++, | |
type: "operator", | |
operator: tail | |
}), | |
expression: result | |
}) | |
} | |
for (var i = 0; i < rest.length; i++) { | |
result = new node({ | |
type: "postfix", | |
operator: rest[i], | |
expression: result | |
}) | |
} | |
return result; | |
} | |
parameter_list | |
= "void" {return []; } | |
/ head:assignment_expression tail:(comma assignment_expression)* { | |
return [ head ].concat(tail.map(function(item) { return item[1] })); | |
} | |
function_call | |
= function_name:function_identifier left_paren | |
parameters:(parameter_list?) right_paren { | |
var result = new node({ | |
type: "function_call", | |
function_name: function_name, | |
parameters: parameters | |
}); | |
if (!parameters) { | |
result.parameters = []; | |
} | |
return result; | |
} | |
function_identifier | |
= id:identifier {return id.name;}/ type_name | |
unary_expression | |
= head:("++" / "--" / "!" / "~" / "+" / "-")? _? | |
tail:postfix_expression_no_repeat { | |
result = tail | |
if (head) { | |
result = new node({ | |
type: "unary", | |
expression: result, | |
operator: new node({ | |
type: "operator", | |
operator: head | |
}) | |
}); | |
} | |
return result; | |
} | |
multiplicative_operator | |
= operator:("*" / "/" / "%") !"=" { | |
return new node({ | |
type: "operator", | |
operator: operator | |
}); | |
} | |
multiplicative_expression | |
= head:unary_expression | |
tail:(_? multiplicative_operator _? unary_expression)* { | |
return daisy_chain(head, tail); | |
} | |
additive_operator | |
= "+" !("+" / "=") { | |
return new node({ | |
type: "operator", | |
operator: "+" | |
}); | |
} | |
/ "-" !("-" / "=") { | |
return new node({ | |
type: "operator", | |
operator: "-" | |
}); | |
} | |
additive_expression | |
= head:multiplicative_expression | |
tail:(_? additive_operator _? multiplicative_expression)* { | |
return daisy_chain(head, tail); | |
} | |
shift_operator | |
= operator:("<<" / ">>") !"=" { | |
return new node({ | |
type: "operator", | |
operator: operator | |
}); | |
} | |
shift_expression | |
= head:additive_expression | |
tail:(_? shift_operator _? additive_expression)* { | |
return daisy_chain(head, tail); | |
} | |
relational_operator | |
= "<" !("<") equal:("=")? { | |
return new node({ | |
type: "operator", | |
operator: "<" + (equal || "") | |
}); | |
} | |
/ ">" !(">") equal:("=")? { | |
return new node({ | |
type: "operator", | |
operator: ">" + (equal || "") | |
}); | |
} | |
relational_expression | |
= head:shift_expression | |
tail:(_? relational_operator _? shift_expression)* { | |
return daisy_chain(head, tail); | |
} | |
equality_operator | |
= operator:("==" / "!=") { | |
return new node({ | |
type: "operator", | |
operator: operator | |
}); | |
} | |
equality_expression | |
= head:relational_expression | |
tail:(_? equality_operator _? relational_expression)* { | |
return daisy_chain(head, tail); | |
} | |
bitwise_and_operator | |
= "&" !("="/"&") { | |
return new node({ | |
type: "operator", | |
operator: "&" | |
}); | |
} | |
bitwise_and_expression | |
= head:equality_expression | |
tail:(_? bitwise_and_operator _? equality_expression)* { | |
return daisy_chain(head, tail); | |
} | |
bitwise_xor_operator | |
= "^" !("="/"^") { | |
return new node({ | |
type: "operator", | |
operator: "^" | |
}); | |
} | |
bitwise_xor_expression | |
= head:bitwise_and_expression | |
tail:(_? bitwise_xor_operator _? bitwise_and_expression)* { | |
return daisy_chain(head, tail); | |
} | |
bitwise_or_operator | |
= "|" !("="/"|") { | |
return new node({ | |
type: "operator", | |
operator: "|" | |
}); | |
} | |
bitwise_or_expression | |
= head:bitwise_xor_expression | |
tail:(_? bitwise_or_operator _? bitwise_xor_expression)* { | |
return daisy_chain(head, tail); | |
} | |
logical_and_operator | |
= "&&" { | |
return new node({ | |
type: "operator", | |
operator: "&&" | |
}); | |
} | |
logical_and_expression | |
= head:bitwise_or_expression | |
tail:(_? logical_and_operator _? bitwise_or_expression)* { | |
return daisy_chain(head, tail); | |
} | |
logical_xor_operator | |
= "^^" { | |
return new node({ | |
type: "operator", | |
operator: "^^" | |
}); | |
} | |
logical_xor_expression | |
= head:logical_and_expression | |
tail:(_? logical_xor_operator _? logical_and_expression)* { | |
return daisy_chain(head, tail); | |
} | |
logical_or_operator | |
= "||" { | |
return new node({ | |
type: "operator", | |
operator: "||" | |
}); | |
} | |
logical_or_expression | |
= head:logical_xor_expression | |
tail:(_? logical_or_operator _? logical_xor_expression)* { | |
return daisy_chain(head, tail); | |
} | |
conditional_expression | |
= head:logical_or_expression | |
tail:(_? "?" _? expression _? ":" _? assignment_expression)? { | |
result = head; | |
if (tail) { | |
result = new node({ | |
type: "ternary", | |
condition: head, | |
is_true: tail[3], | |
is_false: tail[7] | |
}) | |
} | |
return result; | |
} | |
assignment_expression | |
= variable:conditional_expression _? | |
operator:("=" / "*=" / "/=" / "%=" / | |
"+=" / "-=" / "<<=" / ">>=" / | |
"&=" / "^=" / "|=") _? | |
expression:assignment_expression { | |
return new node({ | |
type: "binary", | |
operator: new node({ | |
type: "operator", | |
operator: operator | |
}), | |
left: variable, | |
right: expression | |
}); | |
} | |
/ conditional_expression | |
expression | |
= assignment_expression | |
condition | |
= locally_specified_type _ identifier _? "=" _? initializer | |
/ expression | |
initializer | |
= assignment_expression |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment