ast folding
This commit is contained in:
13
fold.ce
Normal file
13
fold.ce
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
var fd = use("fd")
|
||||||
|
var json = use("json")
|
||||||
|
|
||||||
|
var filename = args[0]
|
||||||
|
var src = text(fd.slurp(filename))
|
||||||
|
var tokenize = use("tokenize")
|
||||||
|
var parse = use("parse")
|
||||||
|
var fold = use("fold")
|
||||||
|
|
||||||
|
var tok_result = tokenize(src, filename)
|
||||||
|
var ast = parse(tok_result.tokens, src, filename, tokenize)
|
||||||
|
var folded = fold(ast)
|
||||||
|
print(json.encode(folded))
|
||||||
968
fold.cm
Normal file
968
fold.cm
Normal file
@@ -0,0 +1,968 @@
|
|||||||
|
// fold.cm — AST optimization pass
|
||||||
|
// Constant folding, constant propagation, dead code elimination
|
||||||
|
|
||||||
|
var fold = function(ast) {
|
||||||
|
var scopes = ast.scopes
|
||||||
|
var nr_scopes = length(scopes)
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Helpers
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
var is_literal = function(expr) {
|
||||||
|
if (expr == null) return false
|
||||||
|
var k = expr.kind
|
||||||
|
return k == "number" || k == "text" || k == "true" || k == "false" || k == "null"
|
||||||
|
}
|
||||||
|
|
||||||
|
var is_pure = function(expr) {
|
||||||
|
if (expr == null) return true
|
||||||
|
var k = expr.kind
|
||||||
|
var i = 0
|
||||||
|
if (k == "number" || k == "text" || k == "true" || k == "false" ||
|
||||||
|
k == "null" || k == "name" || k == "this") return true
|
||||||
|
if (k == "function") return true
|
||||||
|
if (k == "!" || k == "~" || k == "-unary" || k == "+unary") {
|
||||||
|
return is_pure(expr.expression)
|
||||||
|
}
|
||||||
|
if (k == "array") {
|
||||||
|
i = 0
|
||||||
|
while (i < length(expr.list)) {
|
||||||
|
if (!is_pure(expr.list[i])) return false
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (k == "record") {
|
||||||
|
i = 0
|
||||||
|
while (i < length(expr.list)) {
|
||||||
|
if (!is_pure(expr.list[i].right)) return false
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (k == "then") {
|
||||||
|
return is_pure(expr.expression) && is_pure(expr.then) && is_pure(expr.else)
|
||||||
|
}
|
||||||
|
if (k == "==" || k == "!=" || k == "&&" || k == "||") {
|
||||||
|
return is_pure(expr.left) && is_pure(expr.right)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var copy_loc = function(from, to) {
|
||||||
|
to.at = from.at
|
||||||
|
to.from_row = from.from_row
|
||||||
|
to.from_column = from.from_column
|
||||||
|
to.to_row = from.to_row
|
||||||
|
to.to_column = from.to_column
|
||||||
|
return to
|
||||||
|
}
|
||||||
|
|
||||||
|
var make_number = function(val, src) {
|
||||||
|
return copy_loc(src, {kind: "number", value: text(val), number: val})
|
||||||
|
}
|
||||||
|
|
||||||
|
var make_text = function(val, src) {
|
||||||
|
return copy_loc(src, {kind: "text", value: val})
|
||||||
|
}
|
||||||
|
|
||||||
|
var make_bool = function(val, src) {
|
||||||
|
if (val) return copy_loc(src, {kind: "true"})
|
||||||
|
return copy_loc(src, {kind: "false"})
|
||||||
|
}
|
||||||
|
|
||||||
|
var make_null = function(src) {
|
||||||
|
return copy_loc(src, {kind: "null"})
|
||||||
|
}
|
||||||
|
|
||||||
|
var is_truthy_literal = function(expr) {
|
||||||
|
if (expr == null) return null
|
||||||
|
var k = expr.kind
|
||||||
|
var nv = null
|
||||||
|
if (k == "true") return true
|
||||||
|
if (k == "false" || k == "null") return false
|
||||||
|
if (k == "number") {
|
||||||
|
nv = expr.number
|
||||||
|
if (nv == null) nv = number(expr.value)
|
||||||
|
return nv != 0
|
||||||
|
}
|
||||||
|
if (k == "text") return length(expr.value) > 0
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Scope helpers
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
var find_scope = function(fn_nr) {
|
||||||
|
var i = 0
|
||||||
|
while (i < nr_scopes) {
|
||||||
|
if (scopes[i].function_nr == fn_nr) return scopes[i]
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
var scope_var = function(fn_nr, name) {
|
||||||
|
var sc = find_scope(fn_nr)
|
||||||
|
if (sc == null) return null
|
||||||
|
return sc[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
var remove_scope_var = function(fn_nr, name) {
|
||||||
|
var sc = find_scope(fn_nr)
|
||||||
|
if (sc == null) return null
|
||||||
|
delete sc[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Pass 1: pre-scan for constants and function arities
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
var const_defs = {}
|
||||||
|
var fn_arities = {}
|
||||||
|
|
||||||
|
var register_const = function(fn_nr, name, lit_node) {
|
||||||
|
var key = text(fn_nr)
|
||||||
|
if (const_defs[key] == null) const_defs[key] = {}
|
||||||
|
const_defs[key][name] = lit_node
|
||||||
|
}
|
||||||
|
|
||||||
|
var get_const = function(fn_nr, name) {
|
||||||
|
var key = text(fn_nr)
|
||||||
|
if (const_defs[key] == null) return null
|
||||||
|
return const_defs[key][name]
|
||||||
|
}
|
||||||
|
|
||||||
|
var register_arity = function(fn_nr, name, count) {
|
||||||
|
var key = text(fn_nr)
|
||||||
|
if (fn_arities[key] == null) fn_arities[key] = {}
|
||||||
|
fn_arities[key][name] = count
|
||||||
|
}
|
||||||
|
|
||||||
|
var pre_scan_stmts = null
|
||||||
|
var pre_scan_fn = null
|
||||||
|
|
||||||
|
pre_scan_fn = function(node) {
|
||||||
|
if (node == null) return null
|
||||||
|
if (node.statements != null) pre_scan_stmts(node.statements, node.function_nr)
|
||||||
|
if (node.disruption != null) pre_scan_stmts(node.disruption, node.function_nr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pre_scan_stmts = function(stmts, fn_nr) {
|
||||||
|
var i = 0
|
||||||
|
var j = 0
|
||||||
|
var stmt = null
|
||||||
|
var kind = null
|
||||||
|
var name = null
|
||||||
|
var sv = null
|
||||||
|
var item = null
|
||||||
|
while (i < length(stmts)) {
|
||||||
|
stmt = stmts[i]
|
||||||
|
kind = stmt.kind
|
||||||
|
if (kind == "def") {
|
||||||
|
name = stmt.left.name
|
||||||
|
if (name != null && is_literal(stmt.right)) {
|
||||||
|
sv = scope_var(fn_nr, name)
|
||||||
|
if (sv != null && !sv.closure) {
|
||||||
|
register_const(fn_nr, name, stmt.right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (kind == "function") {
|
||||||
|
name = stmt.name
|
||||||
|
if (name != null && stmt.arity != null) {
|
||||||
|
register_arity(fn_nr, name, stmt.arity)
|
||||||
|
}
|
||||||
|
pre_scan_fn(stmt)
|
||||||
|
} else if (kind == "var") {
|
||||||
|
if (stmt.right != null && stmt.right.kind == "function" && stmt.right.arity != null) {
|
||||||
|
name = stmt.left.name
|
||||||
|
if (name != null) {
|
||||||
|
sv = scope_var(fn_nr, name)
|
||||||
|
if (sv != null && sv.make == "var") {
|
||||||
|
register_arity(fn_nr, name, stmt.right.arity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (kind == "var_list") {
|
||||||
|
j = 0
|
||||||
|
while (j < length(stmt.list)) {
|
||||||
|
item = stmt.list[j]
|
||||||
|
if (item.kind == "var" && item.right != null && item.right.kind == "function" && item.right.arity != null) {
|
||||||
|
name = item.left.name
|
||||||
|
if (name != null) {
|
||||||
|
sv = scope_var(fn_nr, name)
|
||||||
|
if (sv != null && sv.make == "var") {
|
||||||
|
register_arity(fn_nr, name, item.right.arity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pre_scan_expr_fns = null
|
||||||
|
pre_scan_expr_fns = function(expr) {
|
||||||
|
if (expr == null) return null
|
||||||
|
var k = expr.kind
|
||||||
|
var i = 0
|
||||||
|
if (k == "function") {
|
||||||
|
pre_scan_fn(expr)
|
||||||
|
}
|
||||||
|
if (expr.left != null) pre_scan_expr_fns(expr.left)
|
||||||
|
if (expr.right != null) pre_scan_expr_fns(expr.right)
|
||||||
|
if (expr.expression != null) pre_scan_expr_fns(expr.expression)
|
||||||
|
if (expr.then != null) pre_scan_expr_fns(expr.then)
|
||||||
|
if (expr.else != null) pre_scan_expr_fns(expr.else)
|
||||||
|
if (k == "(" || k == "array") {
|
||||||
|
i = 0
|
||||||
|
while (i < length(expr.list)) {
|
||||||
|
pre_scan_expr_fns(expr.list[i])
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (k == "record") {
|
||||||
|
i = 0
|
||||||
|
while (i < length(expr.list)) {
|
||||||
|
pre_scan_expr_fns(expr.list[i].right)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pre_scan_stmt_exprs = null
|
||||||
|
pre_scan_stmt_exprs = function(stmts, fn_nr) {
|
||||||
|
var i = 0
|
||||||
|
var j = 0
|
||||||
|
var stmt = null
|
||||||
|
var kind = null
|
||||||
|
while (i < length(stmts)) {
|
||||||
|
stmt = stmts[i]
|
||||||
|
kind = stmt.kind
|
||||||
|
if (kind == "var" || kind == "def") {
|
||||||
|
pre_scan_expr_fns(stmt.right)
|
||||||
|
} else if (kind == "var_list") {
|
||||||
|
j = 0
|
||||||
|
while (j < length(stmt.list)) {
|
||||||
|
pre_scan_expr_fns(stmt.list[j].right)
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
} else if (kind == "call") {
|
||||||
|
pre_scan_expr_fns(stmt.expression)
|
||||||
|
} else if (kind == "if") {
|
||||||
|
pre_scan_expr_fns(stmt.expression)
|
||||||
|
pre_scan_stmt_exprs(stmt.then, fn_nr)
|
||||||
|
pre_scan_stmt_exprs(stmt.list, fn_nr)
|
||||||
|
if (stmt.else != null) pre_scan_stmt_exprs(stmt.else, fn_nr)
|
||||||
|
} else if (kind == "while" || kind == "do") {
|
||||||
|
pre_scan_expr_fns(stmt.expression)
|
||||||
|
pre_scan_stmt_exprs(stmt.statements, fn_nr)
|
||||||
|
} else if (kind == "for") {
|
||||||
|
if (stmt.init != null) {
|
||||||
|
if (stmt.init.kind == "var" || stmt.init.kind == "def") {
|
||||||
|
pre_scan_expr_fns(stmt.init.right)
|
||||||
|
} else {
|
||||||
|
pre_scan_expr_fns(stmt.init)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pre_scan_expr_fns(stmt.test)
|
||||||
|
pre_scan_expr_fns(stmt.update)
|
||||||
|
pre_scan_stmt_exprs(stmt.statements, fn_nr)
|
||||||
|
} else if (kind == "return" || kind == "go") {
|
||||||
|
pre_scan_expr_fns(stmt.expression)
|
||||||
|
} else if (kind == "block") {
|
||||||
|
pre_scan_stmt_exprs(stmt.statements, fn_nr)
|
||||||
|
} else if (kind == "label") {
|
||||||
|
if (stmt.statement != null) {
|
||||||
|
pre_scan_stmt_exprs([stmt.statement], fn_nr)
|
||||||
|
}
|
||||||
|
} else if (kind == "function") {
|
||||||
|
// already handled in pre_scan_stmts
|
||||||
|
null
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pre_scan = function() {
|
||||||
|
pre_scan_stmts(ast.statements, 0)
|
||||||
|
pre_scan_stmts(ast.functions, 0)
|
||||||
|
pre_scan_stmt_exprs(ast.statements, 0)
|
||||||
|
pre_scan_stmt_exprs(ast.functions, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Pass 2: fold expressions and statements
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
var fold_expr = null
|
||||||
|
var fold_stmt = null
|
||||||
|
var fold_stmts = null
|
||||||
|
|
||||||
|
fold_expr = function(expr, fn_nr) {
|
||||||
|
if (expr == null) return null
|
||||||
|
var k = expr.kind
|
||||||
|
var left = null
|
||||||
|
var right = null
|
||||||
|
var lv = null
|
||||||
|
var rv = null
|
||||||
|
var result = null
|
||||||
|
var i = 0
|
||||||
|
var sv = null
|
||||||
|
var lit = null
|
||||||
|
var cond_k = null
|
||||||
|
var ek = null
|
||||||
|
var target = null
|
||||||
|
var ar = null
|
||||||
|
var akey = null
|
||||||
|
var tv = null
|
||||||
|
|
||||||
|
// Recurse into children first (bottom-up)
|
||||||
|
if (k == "+" || k == "-" || k == "*" || k == "/" || k == "%" ||
|
||||||
|
k == "**" || k == "==" || k == "!=" || k == "<" || k == ">" ||
|
||||||
|
k == "<=" || k == ">=" || k == "&" || k == "|" || k == "^" ||
|
||||||
|
k == "<<" || k == ">>" || k == ">>>" || k == "&&" || k == "||" ||
|
||||||
|
k == "," || k == "in") {
|
||||||
|
expr.left = fold_expr(expr.left, fn_nr)
|
||||||
|
expr.right = fold_expr(expr.right, fn_nr)
|
||||||
|
} else if (k == "." || k == "[") {
|
||||||
|
expr.left = fold_expr(expr.left, fn_nr)
|
||||||
|
if (k == "[" && expr.right != null) expr.right = fold_expr(expr.right, fn_nr)
|
||||||
|
} else if (k == "!" || k == "~" || k == "-unary" || k == "+unary" || k == "delete") {
|
||||||
|
expr.expression = fold_expr(expr.expression, fn_nr)
|
||||||
|
} else if (k == "++" || k == "--") {
|
||||||
|
return expr
|
||||||
|
} else if (k == "then") {
|
||||||
|
expr.expression = fold_expr(expr.expression, fn_nr)
|
||||||
|
expr.then = fold_expr(expr.then, fn_nr)
|
||||||
|
expr.else = fold_expr(expr.else, fn_nr)
|
||||||
|
} else if (k == "(") {
|
||||||
|
expr.expression = fold_expr(expr.expression, fn_nr)
|
||||||
|
i = 0
|
||||||
|
while (i < length(expr.list)) {
|
||||||
|
expr.list[i] = fold_expr(expr.list[i], fn_nr)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
} else if (k == "array") {
|
||||||
|
i = 0
|
||||||
|
while (i < length(expr.list)) {
|
||||||
|
expr.list[i] = fold_expr(expr.list[i], fn_nr)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
} else if (k == "record") {
|
||||||
|
i = 0
|
||||||
|
while (i < length(expr.list)) {
|
||||||
|
expr.list[i].right = fold_expr(expr.list[i].right, fn_nr)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
} else if (k == "text literal") {
|
||||||
|
i = 0
|
||||||
|
while (i < length(expr.list)) {
|
||||||
|
expr.list[i] = fold_expr(expr.list[i], fn_nr)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
} else if (k == "function") {
|
||||||
|
fold_fn(expr)
|
||||||
|
return expr
|
||||||
|
} else if (k == "assign" || k == "+=" || k == "-=" || k == "*=" ||
|
||||||
|
k == "/=" || k == "%=" || k == "<<=" || k == ">>=" ||
|
||||||
|
k == ">>>=" || k == "&=" || k == "^=" || k == "|=" ||
|
||||||
|
k == "**=" || k == "&&=" || k == "||=") {
|
||||||
|
expr.right = fold_expr(expr.right, fn_nr)
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constant propagation: name → literal
|
||||||
|
if (k == "name" && expr.level == 0) {
|
||||||
|
lit = get_const(fn_nr, expr.name)
|
||||||
|
if (lit != null) {
|
||||||
|
sv = scope_var(fn_nr, expr.name)
|
||||||
|
if (sv != null && !sv.closure) {
|
||||||
|
return copy_loc(expr, {kind: lit.kind, value: lit.value, number: lit.number})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary constant folding
|
||||||
|
if (k == "+" || k == "-" || k == "*" || k == "/" || k == "%" || k == "**") {
|
||||||
|
left = expr.left
|
||||||
|
right = expr.right
|
||||||
|
if (left != null && right != null && left.kind == "number" && right.kind == "number") {
|
||||||
|
lv = left.number
|
||||||
|
rv = right.number
|
||||||
|
if (lv == null) lv = number(left.value)
|
||||||
|
if (rv == null) rv = number(right.value)
|
||||||
|
if (k == "/") {
|
||||||
|
if (rv == 0) return make_null(expr)
|
||||||
|
}
|
||||||
|
if (k == "%") {
|
||||||
|
if (rv == 0) return make_null(expr)
|
||||||
|
}
|
||||||
|
result = null
|
||||||
|
if (k == "+") result = lv + rv
|
||||||
|
else if (k == "-") result = lv - rv
|
||||||
|
else if (k == "*") result = lv * rv
|
||||||
|
else if (k == "/") result = lv / rv
|
||||||
|
else if (k == "%") result = lv % rv
|
||||||
|
else if (k == "**") result = lv ** rv
|
||||||
|
if (result == null) return make_null(expr)
|
||||||
|
return make_number(result, expr)
|
||||||
|
}
|
||||||
|
// text + text
|
||||||
|
if (k == "+" && left != null && right != null && left.kind == "text" && right.kind == "text") {
|
||||||
|
return make_text(left.value + right.value, expr)
|
||||||
|
}
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparison folding
|
||||||
|
if (k == "==" || k == "!=" || k == "<" || k == ">" || k == "<=" || k == ">=") {
|
||||||
|
left = expr.left
|
||||||
|
right = expr.right
|
||||||
|
if (left != null && right != null) {
|
||||||
|
if (left.kind == "number" && right.kind == "number") {
|
||||||
|
lv = left.number
|
||||||
|
rv = right.number
|
||||||
|
if (lv == null) lv = number(left.value)
|
||||||
|
if (rv == null) rv = number(right.value)
|
||||||
|
if (k == "==") return make_bool(lv == rv, expr)
|
||||||
|
if (k == "!=") return make_bool(lv != rv, expr)
|
||||||
|
if (k == "<") return make_bool(lv < rv, expr)
|
||||||
|
if (k == ">") return make_bool(lv > rv, expr)
|
||||||
|
if (k == "<=") return make_bool(lv <= rv, expr)
|
||||||
|
if (k == ">=") return make_bool(lv >= rv, expr)
|
||||||
|
}
|
||||||
|
if (left.kind == "text" && right.kind == "text") {
|
||||||
|
if (k == "==") return make_bool(left.value == right.value, expr)
|
||||||
|
if (k == "!=") return make_bool(left.value != right.value, expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bitwise folding
|
||||||
|
if (k == "&" || k == "|" || k == "^" || k == "<<" || k == ">>") {
|
||||||
|
left = expr.left
|
||||||
|
right = expr.right
|
||||||
|
if (left != null && right != null && left.kind == "number" && right.kind == "number") {
|
||||||
|
lv = left.number
|
||||||
|
rv = right.number
|
||||||
|
if (lv == null) lv = number(left.value)
|
||||||
|
if (rv == null) rv = number(right.value)
|
||||||
|
if (k == "&") return make_number(lv & rv, expr)
|
||||||
|
if (k == "|") return make_number(lv | rv, expr)
|
||||||
|
if (k == "^") return make_number(lv ^ rv, expr)
|
||||||
|
if (k == "<<") return make_number(lv << rv, expr)
|
||||||
|
if (k == ">>") return make_number(lv >> rv, expr)
|
||||||
|
}
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unary folding
|
||||||
|
if (k == "!") {
|
||||||
|
if (expr.expression != null) {
|
||||||
|
ek = expr.expression.kind
|
||||||
|
if (ek == "true") return make_bool(false, expr)
|
||||||
|
if (ek == "false") return make_bool(true, expr)
|
||||||
|
}
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
if (k == "~") {
|
||||||
|
if (expr.expression != null && expr.expression.kind == "number") {
|
||||||
|
lv = expr.expression.number
|
||||||
|
if (lv == null) lv = number(expr.expression.value)
|
||||||
|
return make_number(~lv, expr)
|
||||||
|
}
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
if (k == "-unary") {
|
||||||
|
if (expr.expression != null && expr.expression.kind == "number") {
|
||||||
|
lv = expr.expression.number
|
||||||
|
if (lv == null) lv = number(expr.expression.value)
|
||||||
|
return make_number(0 - lv, expr)
|
||||||
|
}
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ternary with literal condition
|
||||||
|
if (k == "then") {
|
||||||
|
tv = is_truthy_literal(expr.expression)
|
||||||
|
if (tv == true) return expr.then
|
||||||
|
if (tv == false) return expr.else
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call: stamp arity
|
||||||
|
if (k == "(") {
|
||||||
|
target = expr.expression
|
||||||
|
if (target != null && target.kind == "name" && target.level == 0) {
|
||||||
|
ar = null
|
||||||
|
akey = text(fn_nr)
|
||||||
|
if (fn_arities[akey] != null) ar = fn_arities[akey][target.name]
|
||||||
|
if (ar != null) expr.arity = ar
|
||||||
|
}
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
|
var fold_fn = null
|
||||||
|
|
||||||
|
fold_stmt = function(stmt, fn_nr) {
|
||||||
|
if (stmt == null) return null
|
||||||
|
var k = stmt.kind
|
||||||
|
var i = 0
|
||||||
|
var sv = null
|
||||||
|
var cond_k = null
|
||||||
|
var ik = null
|
||||||
|
var tv = null
|
||||||
|
|
||||||
|
if (k == "var" || k == "def") {
|
||||||
|
stmt.right = fold_expr(stmt.right, fn_nr)
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
if (k == "var_list") {
|
||||||
|
i = 0
|
||||||
|
while (i < length(stmt.list)) {
|
||||||
|
stmt.list[i] = fold_stmt(stmt.list[i], fn_nr)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
if (k == "call") {
|
||||||
|
stmt.expression = fold_expr(stmt.expression, fn_nr)
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
if (k == "if") {
|
||||||
|
stmt.expression = fold_expr(stmt.expression, fn_nr)
|
||||||
|
tv = is_truthy_literal(stmt.expression)
|
||||||
|
if (tv == true) {
|
||||||
|
stmt.then = fold_stmts(stmt.then, fn_nr)
|
||||||
|
return {kind: "block", statements: stmt.then,
|
||||||
|
at: stmt.at, from_row: stmt.from_row, from_column: stmt.from_column,
|
||||||
|
to_row: stmt.to_row, to_column: stmt.to_column}
|
||||||
|
}
|
||||||
|
if (tv == false) {
|
||||||
|
if (stmt.else != null && length(stmt.else) > 0) {
|
||||||
|
stmt.else = fold_stmts(stmt.else, fn_nr)
|
||||||
|
return {kind: "block", statements: stmt.else,
|
||||||
|
at: stmt.at, from_row: stmt.from_row, from_column: stmt.from_column,
|
||||||
|
to_row: stmt.to_row, to_column: stmt.to_column}
|
||||||
|
}
|
||||||
|
if (stmt.list != null && length(stmt.list) > 0) {
|
||||||
|
return fold_stmt(stmt.list[0], fn_nr)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
stmt.then = fold_stmts(stmt.then, fn_nr)
|
||||||
|
stmt.list = fold_stmts(stmt.list, fn_nr)
|
||||||
|
if (stmt.else != null) stmt.else = fold_stmts(stmt.else, fn_nr)
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
if (k == "while") {
|
||||||
|
stmt.expression = fold_expr(stmt.expression, fn_nr)
|
||||||
|
if (stmt.expression.kind == "false" || stmt.expression.kind == "null") return null
|
||||||
|
stmt.statements = fold_stmts(stmt.statements, fn_nr)
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
if (k == "do") {
|
||||||
|
stmt.statements = fold_stmts(stmt.statements, fn_nr)
|
||||||
|
stmt.expression = fold_expr(stmt.expression, fn_nr)
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
if (k == "for") {
|
||||||
|
if (stmt.init != null) {
|
||||||
|
ik = stmt.init.kind
|
||||||
|
if (ik == "var" || ik == "def") {
|
||||||
|
stmt.init = fold_stmt(stmt.init, fn_nr)
|
||||||
|
} else {
|
||||||
|
stmt.init = fold_expr(stmt.init, fn_nr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stmt.test != null) stmt.test = fold_expr(stmt.test, fn_nr)
|
||||||
|
if (stmt.update != null) stmt.update = fold_expr(stmt.update, fn_nr)
|
||||||
|
stmt.statements = fold_stmts(stmt.statements, fn_nr)
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
if (k == "return" || k == "go") {
|
||||||
|
stmt.expression = fold_expr(stmt.expression, fn_nr)
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
if (k == "block") {
|
||||||
|
stmt.statements = fold_stmts(stmt.statements, fn_nr)
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
if (k == "label") {
|
||||||
|
stmt.statement = fold_stmt(stmt.statement, fn_nr)
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
if (k == "function") {
|
||||||
|
fold_fn(stmt)
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
fold_stmts = function(stmts, fn_nr) {
|
||||||
|
var i = 0
|
||||||
|
var stmt = null
|
||||||
|
var out = []
|
||||||
|
var sv = null
|
||||||
|
var name = null
|
||||||
|
while (i < length(stmts)) {
|
||||||
|
stmt = fold_stmt(stmts[i], fn_nr)
|
||||||
|
if (stmt == null) {
|
||||||
|
i = i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Dead code elimination: unused pure var/def
|
||||||
|
if (stmt.kind == "var" || stmt.kind == "def") {
|
||||||
|
name = stmt.left.name
|
||||||
|
if (name != null) {
|
||||||
|
sv = scope_var(fn_nr, name)
|
||||||
|
if (sv != null && sv.nr_uses == 0 && is_pure(stmt.right)) {
|
||||||
|
stmt.dead = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Dead function elimination
|
||||||
|
if (stmt.kind == "function" && stmt.name != null) {
|
||||||
|
sv = scope_var(fn_nr, stmt.name)
|
||||||
|
if (sv != null && sv.nr_uses == 0) {
|
||||||
|
stmt.dead = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stmt.dead != true) push(out, stmt)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
fold_fn = function(node) {
|
||||||
|
if (node == null) return null
|
||||||
|
var fn_nr = node.function_nr
|
||||||
|
if (fn_nr == null) return null
|
||||||
|
// Fold param defaults
|
||||||
|
var i = 0
|
||||||
|
while (i < length(node.list)) {
|
||||||
|
if (node.list[i].expression != null) {
|
||||||
|
node.list[i].expression = fold_expr(node.list[i].expression, fn_nr)
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
if (node.statements != null) node.statements = fold_stmts(node.statements, fn_nr)
|
||||||
|
if (node.disruption != null) node.disruption = fold_stmts(node.disruption, fn_nr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Pass 3: cleanup scopes
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
var cleanup = function() {
|
||||||
|
var i = 0
|
||||||
|
var sc = null
|
||||||
|
var keys = null
|
||||||
|
var j = 0
|
||||||
|
var key = null
|
||||||
|
var entry = null
|
||||||
|
var slots = 0
|
||||||
|
var close_slots = 0
|
||||||
|
|
||||||
|
// Remove dead vars from scope records and recalculate slot counts
|
||||||
|
while (i < nr_scopes) {
|
||||||
|
sc = scopes[i]
|
||||||
|
keys = array(sc)
|
||||||
|
slots = 0
|
||||||
|
close_slots = 0
|
||||||
|
j = 0
|
||||||
|
while (j < length(keys)) {
|
||||||
|
key = keys[j]
|
||||||
|
if (key != "function_nr") {
|
||||||
|
entry = sc[key]
|
||||||
|
if (entry != null && entry.nr_uses == 0 && entry.make != "input") {
|
||||||
|
delete sc[key]
|
||||||
|
} else if (entry != null) {
|
||||||
|
slots = slots + 1
|
||||||
|
if (entry.closure) close_slots = close_slots + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update nr_slots and nr_close_slots on function nodes
|
||||||
|
var update_fn_slots = null
|
||||||
|
update_fn_slots = function(node) {
|
||||||
|
if (node == null) return null
|
||||||
|
var fn_nr = node.function_nr
|
||||||
|
if (fn_nr == null) return null
|
||||||
|
var sc = find_scope(fn_nr)
|
||||||
|
if (sc == null) return null
|
||||||
|
var keys = array(sc)
|
||||||
|
var s = 0
|
||||||
|
var cs = 0
|
||||||
|
var ki = 0
|
||||||
|
var ent = null
|
||||||
|
while (ki < length(keys)) {
|
||||||
|
if (keys[ki] != "function_nr") {
|
||||||
|
ent = sc[keys[ki]]
|
||||||
|
if (ent != null) {
|
||||||
|
s = s + 1
|
||||||
|
if (ent.closure) cs = cs + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ki = ki + 1
|
||||||
|
}
|
||||||
|
node.nr_slots = s
|
||||||
|
node.nr_close_slots = cs
|
||||||
|
}
|
||||||
|
|
||||||
|
var walk_stmts_for_fns = null
|
||||||
|
var walk_expr_for_fns = null
|
||||||
|
|
||||||
|
walk_expr_for_fns = function(expr) {
|
||||||
|
if (expr == null) return null
|
||||||
|
var k = expr.kind
|
||||||
|
var i = 0
|
||||||
|
if (k == "function") {
|
||||||
|
update_fn_slots(expr)
|
||||||
|
walk_stmts_for_fns(expr.statements)
|
||||||
|
walk_stmts_for_fns(expr.disruption)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (expr.left != null) walk_expr_for_fns(expr.left)
|
||||||
|
if (expr.right != null) walk_expr_for_fns(expr.right)
|
||||||
|
if (expr.expression != null) walk_expr_for_fns(expr.expression)
|
||||||
|
if (expr.then != null) walk_expr_for_fns(expr.then)
|
||||||
|
if (expr.else != null) walk_expr_for_fns(expr.else)
|
||||||
|
if (k == "(" || k == "array" || k == "text literal") {
|
||||||
|
i = 0
|
||||||
|
while (i < length(expr.list)) {
|
||||||
|
walk_expr_for_fns(expr.list[i])
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (k == "record") {
|
||||||
|
i = 0
|
||||||
|
while (i < length(expr.list)) {
|
||||||
|
walk_expr_for_fns(expr.list[i].right)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
walk_stmts_for_fns = function(stmts) {
|
||||||
|
if (stmts == null) return null
|
||||||
|
var i = 0
|
||||||
|
var j = 0
|
||||||
|
var stmt = null
|
||||||
|
var k = null
|
||||||
|
while (i < length(stmts)) {
|
||||||
|
stmt = stmts[i]
|
||||||
|
k = stmt.kind
|
||||||
|
if (k == "function") {
|
||||||
|
update_fn_slots(stmt)
|
||||||
|
walk_stmts_for_fns(stmt.statements)
|
||||||
|
walk_stmts_for_fns(stmt.disruption)
|
||||||
|
} else if (k == "var" || k == "def") {
|
||||||
|
walk_expr_for_fns(stmt.right)
|
||||||
|
} else if (k == "var_list") {
|
||||||
|
j = 0
|
||||||
|
while (j < length(stmt.list)) {
|
||||||
|
walk_expr_for_fns(stmt.list[j].right)
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
} else if (k == "call") {
|
||||||
|
walk_expr_for_fns(stmt.expression)
|
||||||
|
} else if (k == "if") {
|
||||||
|
walk_expr_for_fns(stmt.expression)
|
||||||
|
walk_stmts_for_fns(stmt.then)
|
||||||
|
walk_stmts_for_fns(stmt.list)
|
||||||
|
if (stmt.else != null) walk_stmts_for_fns(stmt.else)
|
||||||
|
} else if (k == "while" || k == "do") {
|
||||||
|
walk_expr_for_fns(stmt.expression)
|
||||||
|
walk_stmts_for_fns(stmt.statements)
|
||||||
|
} else if (k == "for") {
|
||||||
|
if (stmt.init != null) {
|
||||||
|
if (stmt.init.kind == "var" || stmt.init.kind == "def") {
|
||||||
|
walk_expr_for_fns(stmt.init.right)
|
||||||
|
} else {
|
||||||
|
walk_expr_for_fns(stmt.init)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
walk_expr_for_fns(stmt.test)
|
||||||
|
walk_expr_for_fns(stmt.update)
|
||||||
|
walk_stmts_for_fns(stmt.statements)
|
||||||
|
} else if (k == "return" || k == "go") {
|
||||||
|
walk_expr_for_fns(stmt.expression)
|
||||||
|
} else if (k == "block") {
|
||||||
|
walk_stmts_for_fns(stmt.statements)
|
||||||
|
} else if (k == "label") {
|
||||||
|
if (stmt.statement != null) walk_stmts_for_fns([stmt.statement])
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
walk_stmts_for_fns(ast.statements)
|
||||||
|
walk_stmts_for_fns(ast.functions)
|
||||||
|
|
||||||
|
// Update intrinsics: collect what's still referenced
|
||||||
|
var used_intrinsics = {}
|
||||||
|
var collect_intrinsics = null
|
||||||
|
var collect_expr_intrinsics = null
|
||||||
|
|
||||||
|
collect_expr_intrinsics = function(expr) {
|
||||||
|
if (expr == null) return null
|
||||||
|
var k = expr.kind
|
||||||
|
var i = 0
|
||||||
|
if (k == "name" && expr.level == -1 && expr.name != null && expr.make != "functino") {
|
||||||
|
used_intrinsics[expr.name] = true
|
||||||
|
}
|
||||||
|
if (expr.left != null) collect_expr_intrinsics(expr.left)
|
||||||
|
if (expr.right != null) collect_expr_intrinsics(expr.right)
|
||||||
|
if (expr.expression != null) collect_expr_intrinsics(expr.expression)
|
||||||
|
if (expr.then != null) collect_expr_intrinsics(expr.then)
|
||||||
|
if (expr.else != null) collect_expr_intrinsics(expr.else)
|
||||||
|
if (k == "(" || k == "array" || k == "text literal") {
|
||||||
|
i = 0
|
||||||
|
while (i < length(expr.list)) {
|
||||||
|
collect_expr_intrinsics(expr.list[i])
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (k == "record") {
|
||||||
|
i = 0
|
||||||
|
while (i < length(expr.list)) {
|
||||||
|
collect_expr_intrinsics(expr.list[i].right)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (k == "function") {
|
||||||
|
collect_intrinsics(expr.statements)
|
||||||
|
collect_intrinsics(expr.disruption)
|
||||||
|
i = 0
|
||||||
|
while (i < length(expr.list)) {
|
||||||
|
if (expr.list[i].expression != null) {
|
||||||
|
collect_expr_intrinsics(expr.list[i].expression)
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
collect_intrinsics = function(stmts) {
|
||||||
|
if (stmts == null) return null
|
||||||
|
var i = 0
|
||||||
|
var j = 0
|
||||||
|
var pi = 0
|
||||||
|
var stmt = null
|
||||||
|
var k = null
|
||||||
|
while (i < length(stmts)) {
|
||||||
|
stmt = stmts[i]
|
||||||
|
k = stmt.kind
|
||||||
|
if (k == "var" || k == "def") {
|
||||||
|
collect_expr_intrinsics(stmt.right)
|
||||||
|
} else if (k == "var_list") {
|
||||||
|
j = 0
|
||||||
|
while (j < length(stmt.list)) {
|
||||||
|
collect_expr_intrinsics(stmt.list[j].right)
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
} else if (k == "call") {
|
||||||
|
collect_expr_intrinsics(stmt.expression)
|
||||||
|
} else if (k == "if") {
|
||||||
|
collect_expr_intrinsics(stmt.expression)
|
||||||
|
collect_intrinsics(stmt.then)
|
||||||
|
collect_intrinsics(stmt.list)
|
||||||
|
if (stmt.else != null) collect_intrinsics(stmt.else)
|
||||||
|
} else if (k == "while" || k == "do") {
|
||||||
|
collect_expr_intrinsics(stmt.expression)
|
||||||
|
collect_intrinsics(stmt.statements)
|
||||||
|
} else if (k == "for") {
|
||||||
|
if (stmt.init != null) {
|
||||||
|
if (stmt.init.kind == "var" || stmt.init.kind == "def") {
|
||||||
|
collect_expr_intrinsics(stmt.init.right)
|
||||||
|
} else {
|
||||||
|
collect_expr_intrinsics(stmt.init)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
collect_expr_intrinsics(stmt.test)
|
||||||
|
collect_expr_intrinsics(stmt.update)
|
||||||
|
collect_intrinsics(stmt.statements)
|
||||||
|
} else if (k == "return" || k == "go") {
|
||||||
|
collect_expr_intrinsics(stmt.expression)
|
||||||
|
} else if (k == "function") {
|
||||||
|
collect_intrinsics(stmt.statements)
|
||||||
|
collect_intrinsics(stmt.disruption)
|
||||||
|
pi = 0
|
||||||
|
while (pi < length(stmt.list)) {
|
||||||
|
if (stmt.list[pi].expression != null) {
|
||||||
|
collect_expr_intrinsics(stmt.list[pi].expression)
|
||||||
|
}
|
||||||
|
pi = pi + 1
|
||||||
|
}
|
||||||
|
} else if (k == "block") {
|
||||||
|
collect_intrinsics(stmt.statements)
|
||||||
|
} else if (k == "label") {
|
||||||
|
if (stmt.statement != null) collect_intrinsics([stmt.statement])
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
collect_intrinsics(ast.statements)
|
||||||
|
collect_intrinsics(ast.functions)
|
||||||
|
|
||||||
|
var new_intrinsics = []
|
||||||
|
i = 0
|
||||||
|
while (i < length(ast.intrinsics)) {
|
||||||
|
if (used_intrinsics[ast.intrinsics[i]] == true) {
|
||||||
|
push(new_intrinsics, ast.intrinsics[i])
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
ast.intrinsics = new_intrinsics
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Main
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
pre_scan()
|
||||||
|
|
||||||
|
// Pass 2: fold all statements and functions
|
||||||
|
ast.statements = fold_stmts(ast.statements, 0)
|
||||||
|
var fi = 0
|
||||||
|
while (fi < length(ast.functions)) {
|
||||||
|
fold_fn(ast.functions[fi])
|
||||||
|
fi = fi + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove dead top-level functions
|
||||||
|
var live_fns = []
|
||||||
|
var fn = null
|
||||||
|
fi = 0
|
||||||
|
while (fi < length(ast.functions)) {
|
||||||
|
fn = ast.functions[fi]
|
||||||
|
if (fn.dead != true) {
|
||||||
|
push(live_fns, fn)
|
||||||
|
}
|
||||||
|
fi = fi + 1
|
||||||
|
}
|
||||||
|
ast.functions = live_fns
|
||||||
|
|
||||||
|
// Pass 3: cleanup
|
||||||
|
cleanup()
|
||||||
|
|
||||||
|
return ast
|
||||||
|
}
|
||||||
|
|
||||||
|
return fold
|
||||||
@@ -24,8 +24,10 @@ function use_basic(path) {
|
|||||||
|
|
||||||
var tok_path = core_path + "/tokenize.cm"
|
var tok_path = core_path + "/tokenize.cm"
|
||||||
var par_path = core_path + "/parse.cm"
|
var par_path = core_path + "/parse.cm"
|
||||||
|
var fold_path = core_path + "/fold.cm"
|
||||||
var tokenize_mod = mach_eval("tokenize", text(fd.slurp(tok_path)), {use: use_basic})
|
var tokenize_mod = mach_eval("tokenize", text(fd.slurp(tok_path)), {use: use_basic})
|
||||||
var parse_mod = mach_eval("parse", text(fd.slurp(par_path)), {use: use_basic})
|
var parse_mod = mach_eval("parse", text(fd.slurp(par_path)), {use: use_basic})
|
||||||
|
var fold_mod = mach_eval("fold", text(fd.slurp(fold_path)), {use: use_basic})
|
||||||
|
|
||||||
// Optionally load mcode compiler module
|
// Optionally load mcode compiler module
|
||||||
var mcode_mod = null
|
var mcode_mod = null
|
||||||
@@ -66,6 +68,7 @@ function analyze(src, filename) {
|
|||||||
}
|
}
|
||||||
disrupt
|
disrupt
|
||||||
}
|
}
|
||||||
|
ast = fold_mod(ast)
|
||||||
return ast
|
return ast
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
77
parse.cm
77
parse.cm
@@ -403,6 +403,7 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
if (tok.kind == ")") advance()
|
if (tok.kind == ")") advance()
|
||||||
else if (tok.kind == "eof") parse_error(tok, "unterminated method parameter list")
|
else if (tok.kind == "eof") parse_error(tok, "unterminated method parameter list")
|
||||||
if (length(params) > 4) parse_error(tok, "functions cannot have more than 4 parameters")
|
if (length(params) > 4) parse_error(tok, "functions cannot have more than 4 parameters")
|
||||||
|
fn.arity = length(params)
|
||||||
if (tok.kind == "{") {
|
if (tok.kind == "{") {
|
||||||
advance()
|
advance()
|
||||||
fn.statements = parse_block_statements()
|
fn.statements = parse_block_statements()
|
||||||
@@ -843,6 +844,7 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (length(params) > 4) parse_error(tok, "functions cannot have more than 4 parameters")
|
if (length(params) > 4) parse_error(tok, "functions cannot have more than 4 parameters")
|
||||||
|
node.arity = length(params)
|
||||||
|
|
||||||
if (tok.kind == "{") {
|
if (tok.kind == "{") {
|
||||||
advance()
|
advance()
|
||||||
@@ -935,6 +937,7 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (length(params) > 4) parse_error(tok, "functions cannot have more than 4 parameters")
|
if (length(params) > 4) parse_error(tok, "functions cannot have more than 4 parameters")
|
||||||
|
node.arity = length(params)
|
||||||
|
|
||||||
if (tok.kind != "=>") {
|
if (tok.kind != "=>") {
|
||||||
parse_error(tok, "expected '=>' in arrow function")
|
parse_error(tok, "expected '=>' in arrow function")
|
||||||
@@ -1274,7 +1277,6 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
var sem_errors = []
|
var sem_errors = []
|
||||||
var scopes_array = []
|
var scopes_array = []
|
||||||
var intrinsics = []
|
var intrinsics = []
|
||||||
var block_var_counter = 0
|
|
||||||
|
|
||||||
var sem_error = function(node, msg) {
|
var sem_error = function(node, msg) {
|
||||||
var err = {message: msg}
|
var err = {message: msg}
|
||||||
@@ -1289,15 +1291,13 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
vars: [],
|
vars: [],
|
||||||
in_loop: opts.in_loop == true,
|
in_loop: opts.in_loop == true,
|
||||||
function_nr: fn_nr,
|
function_nr: fn_nr,
|
||||||
is_function_scope: opts.is_func == true,
|
is_function_scope: opts.is_func == true
|
||||||
block_depth: opts.bdepth != null ? opts.bdepth : 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var sem_add_var = function(scope, name, make_opts) {
|
var sem_add_var = function(scope, name, make_opts) {
|
||||||
push(scope.vars, {
|
push(scope.vars, {
|
||||||
name: name,
|
name: name,
|
||||||
scope_name: null,
|
|
||||||
is_const: make_opts.is_const == true,
|
is_const: make_opts.is_const == true,
|
||||||
make: make_opts.make,
|
make: make_opts.make,
|
||||||
function_nr: make_opts.fn_nr,
|
function_nr: make_opts.fn_nr,
|
||||||
@@ -1364,23 +1364,10 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
return functino_names[name] == true
|
return functino_names[name] == true
|
||||||
}
|
}
|
||||||
|
|
||||||
var sem_propagate_block_vars = function(parent, block) {
|
var sem_propagate_vars = function(parent, child) {
|
||||||
var i = 0
|
var i = 0
|
||||||
var v = null
|
while (i < length(child.vars)) {
|
||||||
var sn = null
|
push(parent.vars, child.vars[i])
|
||||||
while (i < length(block.vars)) {
|
|
||||||
v = block.vars[i]
|
|
||||||
sn = v.scope_name
|
|
||||||
if (sn == null) sn = v.name
|
|
||||||
push(parent.vars, {
|
|
||||||
name: sn,
|
|
||||||
scope_name: null,
|
|
||||||
is_const: v.is_const,
|
|
||||||
make: v.make,
|
|
||||||
function_nr: v.function_nr,
|
|
||||||
nr_uses: v.nr_uses,
|
|
||||||
closure: v.closure
|
|
||||||
})
|
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1471,7 +1458,6 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
if (r.v != null) {
|
if (r.v != null) {
|
||||||
left_node.level = r.level
|
left_node.level = r.level
|
||||||
left_node.function_nr = r.def_function_nr
|
left_node.function_nr = r.def_function_nr
|
||||||
if (r.v.scope_name != null) left_node.scope_name = r.v.scope_name
|
|
||||||
} else {
|
} else {
|
||||||
left_node.level = -1
|
left_node.level = -1
|
||||||
}
|
}
|
||||||
@@ -1525,7 +1511,6 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
if (r.v != null) {
|
if (r.v != null) {
|
||||||
operand.level = r.level
|
operand.level = r.level
|
||||||
operand.function_nr = r.def_function_nr
|
operand.function_nr = r.def_function_nr
|
||||||
if (r.v.scope_name != null) operand.scope_name = r.v.scope_name
|
|
||||||
} else {
|
} else {
|
||||||
operand.level = -1
|
operand.level = -1
|
||||||
}
|
}
|
||||||
@@ -1647,7 +1632,6 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
expr.function_nr = r.def_function_nr
|
expr.function_nr = r.def_function_nr
|
||||||
r.v.nr_uses = r.v.nr_uses + 1
|
r.v.nr_uses = r.v.nr_uses + 1
|
||||||
if (r.level > 0) r.v.closure = 1
|
if (r.level > 0) r.v.closure = 1
|
||||||
if (r.v.scope_name != null) expr.scope_name = r.v.scope_name
|
|
||||||
} else {
|
} else {
|
||||||
expr.level = -1
|
expr.level = -1
|
||||||
sem_add_intrinsic(name)
|
sem_add_intrinsic(name)
|
||||||
@@ -1664,15 +1648,10 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
var name = null
|
var name = null
|
||||||
var existing = null
|
var existing = null
|
||||||
var i = 0
|
var i = 0
|
||||||
var sn = null
|
|
||||||
var then_scope = null
|
|
||||||
var list_scope = null
|
|
||||||
var else_scope = null
|
|
||||||
var loop_scope = null
|
var loop_scope = null
|
||||||
var do_scope = null
|
var do_scope = null
|
||||||
var for_scope = null
|
var for_scope = null
|
||||||
var init_kind = null
|
var init_kind = null
|
||||||
var blk_scope = null
|
|
||||||
var fn_nr_val = null
|
var fn_nr_val = null
|
||||||
var fn_scope = null
|
var fn_scope = null
|
||||||
var pname = null
|
var pname = null
|
||||||
@@ -1695,15 +1674,9 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
if (existing != null && existing.is_const) {
|
if (existing != null && existing.is_const) {
|
||||||
sem_error(stmt.left, "cannot redeclare constant '" + name + "'")
|
sem_error(stmt.left, "cannot redeclare constant '" + name + "'")
|
||||||
}
|
}
|
||||||
if (existing == null || existing.function_nr != scope.function_nr || scope.block_depth > 0) {
|
if (existing == null || existing.function_nr != scope.function_nr) {
|
||||||
sem_add_var(scope, name, {make: "var", fn_nr: scope.function_nr})
|
sem_add_var(scope, name, {make: "var", fn_nr: scope.function_nr})
|
||||||
}
|
}
|
||||||
if (scope.block_depth > 0) {
|
|
||||||
sn = "_" + name + "_" + text(block_var_counter)
|
|
||||||
block_var_counter = block_var_counter + 1
|
|
||||||
scope.vars[length(scope.vars) - 1].scope_name = sn
|
|
||||||
stmt.left.scope_name = sn
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sem_check_expr(scope, stmt.right)
|
sem_check_expr(scope, stmt.right)
|
||||||
return null
|
return null
|
||||||
@@ -1720,12 +1693,6 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
existing.make = "def"
|
existing.make = "def"
|
||||||
} else {
|
} else {
|
||||||
sem_add_var(scope, name, {is_const: true, make: "def", fn_nr: scope.function_nr})
|
sem_add_var(scope, name, {is_const: true, make: "def", fn_nr: scope.function_nr})
|
||||||
if (scope.block_depth > 0) {
|
|
||||||
sn = "_" + name + "_" + text(block_var_counter)
|
|
||||||
block_var_counter = block_var_counter + 1
|
|
||||||
scope.vars[length(scope.vars) - 1].scope_name = sn
|
|
||||||
stmt.left.scope_name = sn
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sem_check_expr(scope, stmt.right)
|
sem_check_expr(scope, stmt.right)
|
||||||
@@ -1739,58 +1706,52 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
|
|
||||||
if (kind == "if") {
|
if (kind == "if") {
|
||||||
sem_check_expr(scope, stmt.expression)
|
sem_check_expr(scope, stmt.expression)
|
||||||
then_scope = make_scope(scope, scope.function_nr, {bdepth: scope.block_depth + 1})
|
|
||||||
i = 0
|
i = 0
|
||||||
while (i < length(stmt.then)) {
|
while (i < length(stmt.then)) {
|
||||||
sem_check_stmt(then_scope, stmt.then[i])
|
sem_check_stmt(scope, stmt.then[i])
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
sem_propagate_block_vars(scope, then_scope)
|
|
||||||
list_scope = make_scope(scope, scope.function_nr, {bdepth: scope.block_depth + 1})
|
|
||||||
i = 0
|
i = 0
|
||||||
while (i < length(stmt.list)) {
|
while (i < length(stmt.list)) {
|
||||||
sem_check_stmt(list_scope, stmt.list[i])
|
sem_check_stmt(scope, stmt.list[i])
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
sem_propagate_block_vars(scope, list_scope)
|
|
||||||
if (stmt.else != null) {
|
if (stmt.else != null) {
|
||||||
else_scope = make_scope(scope, scope.function_nr, {bdepth: scope.block_depth + 1})
|
|
||||||
i = 0
|
i = 0
|
||||||
while (i < length(stmt.else)) {
|
while (i < length(stmt.else)) {
|
||||||
sem_check_stmt(else_scope, stmt.else[i])
|
sem_check_stmt(scope, stmt.else[i])
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
sem_propagate_block_vars(scope, else_scope)
|
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kind == "while") {
|
if (kind == "while") {
|
||||||
sem_check_expr(scope, stmt.expression)
|
sem_check_expr(scope, stmt.expression)
|
||||||
loop_scope = make_scope(scope, scope.function_nr, {in_loop: true, bdepth: scope.block_depth + 1})
|
loop_scope = make_scope(scope, scope.function_nr, {in_loop: true})
|
||||||
i = 0
|
i = 0
|
||||||
while (i < length(stmt.statements)) {
|
while (i < length(stmt.statements)) {
|
||||||
sem_check_stmt(loop_scope, stmt.statements[i])
|
sem_check_stmt(loop_scope, stmt.statements[i])
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
sem_propagate_block_vars(scope, loop_scope)
|
sem_propagate_vars(scope, loop_scope)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kind == "do") {
|
if (kind == "do") {
|
||||||
do_scope = make_scope(scope, scope.function_nr, {in_loop: true, bdepth: scope.block_depth + 1})
|
do_scope = make_scope(scope, scope.function_nr, {in_loop: true})
|
||||||
i = 0
|
i = 0
|
||||||
while (i < length(stmt.statements)) {
|
while (i < length(stmt.statements)) {
|
||||||
sem_check_stmt(do_scope, stmt.statements[i])
|
sem_check_stmt(do_scope, stmt.statements[i])
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
sem_propagate_block_vars(scope, do_scope)
|
sem_propagate_vars(scope, do_scope)
|
||||||
sem_check_expr(scope, stmt.expression)
|
sem_check_expr(scope, stmt.expression)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kind == "for") {
|
if (kind == "for") {
|
||||||
for_scope = make_scope(scope, scope.function_nr, {in_loop: true, bdepth: scope.block_depth + 1})
|
for_scope = make_scope(scope, scope.function_nr, {in_loop: true})
|
||||||
if (stmt.init != null) {
|
if (stmt.init != null) {
|
||||||
init_kind = stmt.init.kind
|
init_kind = stmt.init.kind
|
||||||
if (init_kind == "var" || init_kind == "def") {
|
if (init_kind == "var" || init_kind == "def") {
|
||||||
@@ -1806,7 +1767,7 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
sem_check_stmt(for_scope, stmt.statements[i])
|
sem_check_stmt(for_scope, stmt.statements[i])
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
sem_propagate_block_vars(scope, for_scope)
|
sem_propagate_vars(scope, for_scope)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1834,13 +1795,11 @@ var parse = function(tokens, src, filename, tokenizer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (kind == "block") {
|
if (kind == "block") {
|
||||||
blk_scope = make_scope(scope, scope.function_nr, {bdepth: scope.block_depth + 1})
|
|
||||||
i = 0
|
i = 0
|
||||||
while (i < length(stmt.statements)) {
|
while (i < length(stmt.statements)) {
|
||||||
sem_check_stmt(blk_scope, stmt.statements[i])
|
sem_check_stmt(scope, stmt.statements[i])
|
||||||
i = i + 1
|
i = i + 1
|
||||||
}
|
}
|
||||||
sem_propagate_block_vars(scope, blk_scope)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user