#!/usr/bin/env python3 -B
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import re
quoted_pat = re.compile(r"([^A-Za-z0-9]|^)'([^']+)'")
js_pat = re.compile(r"([^A-Za-z0-9]|^)(JS[A-Z0-9_\*]+)")
def codify(text):
text = re.sub(quoted_pat, "\\1\\2
", text)
text = re.sub(js_pat, "\\1\\2
", text)
return text
space_star_space_pat = re.compile(r"^\s*\* ?", re.M)
def get_comment_body(comment):
return re.sub(space_star_space_pat, "", comment).split("\n")
quote_pat = re.compile('"([^"]+)"')
str_pat = re.compile("js_([^_]+)_str")
def parse_name(s):
m = quote_pat.search(s)
if m:
return m.group(1)
m = str_pat.search(s)
if m:
return m.group(1)
return s
csv_pat = re.compile(", *")
def parse_csv(s):
a = csv_pat.split(s)
if len(a) == 1 and a[0] == "":
return []
return a
def get_stack_count(stack):
if stack == "":
return 0
if "..." in stack:
return -1
return len(stack.split(","))
def parse_index(comment):
index = []
current_types = None
category_name = ""
category_pat = re.compile(r"\[([^\]]+)\]")
for line in get_comment_body(comment):
m = category_pat.search(line)
if m:
category_name = m.group(1)
if category_name == "Index":
continue
current_types = []
index.append((category_name, current_types))
else:
type_name = line.strip()
if type_name and current_types is not None:
current_types.append((type_name, []))
return index
# Holds the information stored in the comment with the following format:
# /*
# * {desc}
# * Category: {category_name}
# * Type: {type_name}
# * Operands: {operands}
# * Stack: {stack_uses} => {stack_defs}
# */
class CommentInfo:
def __init__(self):
self.desc = ""
self.category_name = ""
self.type_name = ""
self.operands = ""
self.stack_uses = ""
self.stack_defs = ""
# Holds the information stored in the macro with the following format:
# MACRO({op}, {op_snake}, {token}, {length}, {nuses}, {ndefs}, {format})
# and the information from CommentInfo.
class OpcodeInfo:
def __init__(self, value, comment_info):
self.op = ""
self.op_snake = ""
self.value = value
self.token = ""
self.length = ""
self.nuses = ""
self.ndefs = ""
self.format_ = ""
self.operands_array = []
self.stack_uses_array = []
self.stack_defs_array = []
self.desc = comment_info.desc
self.category_name = comment_info.category_name
self.type_name = comment_info.type_name
self.operands = comment_info.operands
self.operands_array = comment_info.operands_array
self.stack_uses = comment_info.stack_uses
self.stack_uses_array = comment_info.stack_uses_array
self.stack_defs = comment_info.stack_defs
self.stack_defs_array = comment_info.stack_defs_array
# List of OpcodeInfo that corresponds to macros after this.
# /*
# * comment
# */
# MACRO(Sub, ...)
# MACRO(Mul, ...)
# MACRO(Div, ...)
self.group = []
self.sort_key = ""
def find_by_name(list, name):
for n, body in list:
if n == name:
return body
return None
def add_to_index(index, opcode):
types = find_by_name(index, opcode.category_name)
if types is None:
raise Exception(
"Category is not listed in index: "
"{name}".format(name=opcode.category_name)
)
opcodes = find_by_name(types, opcode.type_name)
if opcodes is None:
if opcode.type_name:
raise Exception(
"Type is not listed in {category}: "
"{name}".format(category=opcode.category_name, name=opcode.type_name)
)
types.append((opcode.type_name, [opcode]))
return
opcodes.append(opcode)
tag_pat = re.compile(r"^\s*[A-Za-z]+:\s*|\s*$")
def get_tag_value(line):
return re.sub(tag_pat, "", line)
RUST_OR_CPP_KEYWORDS = {
"and",
"case",
"default",
"double",
"false",
"goto",
"in",
"new",
"not",
"or",
"return",
"throw",
"true",
"try",
"typeof",
"void",
}
def get_opcodes(dir):
iter_pat = re.compile(
r"/\*(.*?)\*/" # either a documentation comment...
r"|"
r"MACRO\(" # or a MACRO(...) call
r"(?P[^,]+),\s*"
r"(?P[^,]+),\s*"
r"(?P[^,]+,)\s*"
r"(?P[0-9\-]+),\s*"
r"(?P[0-9\-]+),\s*"
r"(?P[0-9\-]+),\s*"
r"(?P[^\)]+)"
r"\)",
re.S,
)
stack_pat = re.compile(r"^(?P.*?)" r"\s*=>\s*" r"(?P.*?)$")
opcodes = dict()
index = []
with open("{dir}/js/src/vm/Opcodes.h".format(dir=dir), "r", encoding="utf-8") as f:
data = f.read()
comment_info = None
opcode = None
# The first opcode after the comment.
group_head = None
next_opcode_value = 0
for m in re.finditer(iter_pat, data):
comment = m.group(1)
op = m.group("op")
if comment:
if "[Index]" in comment:
index = parse_index(comment)
continue
if "Operands:" not in comment:
continue
group_head = None
comment_info = CommentInfo()
state = "desc"
stack = ""
desc = ""
for line in get_comment_body(comment):
if line.startswith(" Category:"):
state = "category"
comment_info.category_name = get_tag_value(line)
elif line.startswith(" Type:"):
state = "type"
comment_info.type_name = get_tag_value(line)
elif line.startswith(" Operands:"):
state = "operands"
comment_info.operands = get_tag_value(line)
elif line.startswith(" Stack:"):
state = "stack"
stack = get_tag_value(line)
elif state == "desc":
desc += line + "\n"
elif line.startswith(" "):
if line.isspace():
pass
elif state == "operands":
comment_info.operands += " " + line.strip()
elif state == "stack":
stack += " " + line.strip()
else:
raise ValueError(
"unrecognized line in comment: {!r}\n\nfull comment was:\n{}".format(
line, comment
)
)
comment_info.desc = desc
comment_info.operands_array = parse_csv(comment_info.operands)
comment_info.stack_uses_array = parse_csv(comment_info.stack_uses)
comment_info.stack_defs_array = parse_csv(comment_info.stack_defs)
m2 = stack_pat.search(stack)
if m2:
comment_info.stack_uses = m2.group("uses")
comment_info.stack_defs = m2.group("defs")
else:
assert op is not None
opcode = OpcodeInfo(next_opcode_value, comment_info)
next_opcode_value += 1
opcode.op = op
opcode.op_snake = m.group("op_snake")
opcode.token = parse_name(m.group("token"))
opcode.length = m.group("length")
opcode.nuses = m.group("nuses")
opcode.ndefs = m.group("ndefs")
opcode.format_ = m.group("format").split("|")
expected_snake = re.sub(r"(?