openhsp-gc を使って簡易インタプリタ (1)

openhsp-gc と後ろで定義されている関数を呼び出せるようにするパッチのテストとしてちょっとしたインタプリタを作ってみたその途中経過です。openhsp-gc を使って再帰下降パーサの拡張です。

まだ変数代入とif文くらいしかないのであまり面白くありません。

通常の HSP や OpenHSP では動かすことのできないプログラムです。バイナリを公開したのでこちらでお試しください: openhsp-gc-bin.zip

#define global null null_@
dimtype null_, vartype("struct")

#enum global node_type_literal = 1
#enum global node_type_binaryop
#enum global node_type_unaryop
#enum global node_type_varref
#enum global node_type_assign
#enum global node_type_list
#enum global node_type_if

#enum global optype_eq = 256
#enum global optype_ne
#enum global optype_lteq
#enum global optype_gteq
#enum global optype_and
#enum global optype_or
#enum global optype_lsh
#enum global optype_rsh

#module mod_node m_type, m_val, m_children
#modcfunc node_get_type
	return m_type
#modcfunc node_get_val
	return m_val
#modcfunc node_get_child int n
	return m_children(n)
#modcfunc node_get_lhs
	return m_children(0)
#modcfunc node_get_rhs
	return m_children(1)
#modfunc node_set_type int type
	m_type = type
	return
#modfunc node_set_val int val
	m_val = val
	return
#modfunc node_set_val_str str val
	m_val = val
	return
#modfunc node_set_child int n, struct node
	m_children(n) = node
	return
#modfunc node_set_lhs struct node
	m_children(0) = node
	return
#modfunc node_set_rhs struct node
	m_children(1) = node
	return
#defcfunc new_node local instance
	newmod instance, mod_node
	return instance
#defcfunc new_literal_node int val, local instance
	instance = new_node()
	node_set_type instance, node_type_literal
	node_set_val instance, val
	return instance
#defcfunc new_binaryop_node int operator, struct lhs, struct rhs, local instance
	instance = new_node()
	node_set_type instance, node_type_binaryop
	node_set_val instance, operator
	node_set_lhs instance, lhs
	node_set_rhs instance, rhs
	return instance
#defcfunc new_unaryop_node int operator, struct lhs, local instance
	instance = new_node()
	node_set_type instance, node_type_unaryop
	node_set_val instance, operator
	node_set_lhs instance, lhs
	return instance
#defcfunc new_varref_node str name, local instance
	instance = new_node()
	node_set_type instance, node_type_varref
	node_set_val_str instance, name
	return instance
#defcfunc new_assign_node str name, struct rhs, local instance
	instance = new_node()
	node_set_type instance, node_type_assign
	node_set_val_str instance, name
	node_set_child instance, 0, rhs
	return instance
#defcfunc new_list_node local instance
	instance = new_node()
	node_set_type instance, node_type_list
	node_set_val instance, 0 // length
	return instance
#defcfunc new_list_node_1 struct child, local instance
	instance = new_list_node()
	append_list_node instance, child
	return instance
#modfunc append_list_node struct child
	node_set_child thismod, node_get_val(thismod), child
	node_set_val thismod, node_get_val(thismod) + 1
	return
#modcfunc get_list_node_len
	return node_get_val(thismod)
#defcfunc new_if_node struct cond, struct then_part, struct else_part, local instance
	instance = new_node()
	node_set_type instance, node_type_if
	node_set_child instance, 0, cond
	node_set_child instance, 1, then_part
	node_set_child instance, 2, else_part
	return instance
#modcfunc get_if_node_cond
	return node_get_child(thismod, 0)
#modcfunc get_if_node_then
	return node_get_child(thismod, 1)
#modcfunc get_if_node_else
	return node_get_child(thismod, 2)
#modcfunc inspect_node local lhs, local rhs
	if thismod == null {
		return "null"
	}
	switch m_type
	case node_type_literal
		return str(m_val)
	case node_type_binaryop
		lhs = inspect_node(node_get_lhs(thismod))
		rhs = inspect_node(node_get_rhs(thismod))
		return strf("(%s %s %s)", optype_to_str(m_val), lhs, rhs)
	case node_type_unaryop
		lhs = inspect_node(node_get_lhs(thismod))
		return strf("(%s %s)", optype_to_str(m_val), lhs)
	case node_type_varref
		return m_val
	case node_type_assign
		return strf("(= %s %s)", m_val, inspect_node(node_get_child(thismod, 0)))
	case node_type_list
		return inspect_node_list(thismod)
	case node_type_if
		return inspect_if_node(thismod)
	default
		assert 0
	swend
#modcfunc inspect_node_list local result
	result = "["
	repeat get_list_node_len(thismod)
		if cnt != 0 : result += ", "
		result += inspect_node(node_get_child(thismod, cnt))
	loop
	result += "]"
	return result
#modcfunc inspect_if_node local cond, local then_part, local else_part
	cond = inspect_node(get_if_node_cond(thismod))
	then_part = inspect_node(get_if_node_then(thismod))
	else_part = inspect_node(get_if_node_else(thismod))
	return strf("(if %s %s %s)", cond, then_part, else_part)
#defcfunc optype_to_str int optype
	if optype < 256 : return strf("%c", optype)
	switch optype
#define ctype CT(%1,%2) case optype_%1: return %2 
	CT(eq, "==")
	CT(ne, "!=")
	CT(lteq, "<=")
	CT(gteq, ">=")
	CT(and, "&&")
	CT(or, "||")
	CT(lsh, "<<")
	CT(rsh, "<<")
#undef CT
	swend
	assert 0
#global

#module mod_dict m_dc
#modinit
	newcom m_dc, "Scripting.Dictionary"
	m_dc("compareMode") = 0
	return
#modfunc dict_put str key, int val
	m_dc("Item", key) = val
	return
#modfunc dict_putv str key, var val
	m_dc("Item", key) = val
	return
#modcfunc dict_get str key
	return m_dc("Item", key)
#modfunc dict_remove str key
	m_dc->"Remove" key
	return
#modcfunc dict_has str key
	return m_dc("Exists", key)
#modcfunc dict_size
	return m_dc("Count")
#modfunc dict_clear
	m_dc->"RemoveAll"
	return
#defcfunc new_dict local instance
	newmod instance, mod_dict
	return instance
#global

#module mod_parser m_src, m_pos, m_type, m_val, m_reserved
#enum token_type_num = 256
#enum token_type_ident
#enum token_type_eq
#enum token_type_ne
#enum token_type_lteq
#enum token_type_gteq
#enum token_type_and
#enum token_type_or
#enum token_type_lsh
#enum token_type_rsh
#enum token_type_if
#enum token_type_else
#enum token_type_end
#enum token_type_error
#modinit str src
	m_src = src
	m_pos = 0
	m_reserved = new_dict()
	dict_put m_reserved, "if", token_type_if
	dict_put m_reserved, "else", token_type_else
	return
#define gettoken parser_gettoken thismod
#modfunc parser_gettoken
	while isspace(peek(m_src, m_pos))
		m_pos ++
	wend
	c = peek(m_src, m_pos)
	if isdigit(c) {
		m_type = token_type_num
		m_val = 0
		repeat
			c = peek(m_src, m_pos)
			if isdigit(c) == 0 : break
			m_val = m_val * 10 + (c - '0')
			m_pos ++
		loop
		return
	}
	if ('A' <= c and c <= 'Z') or c == '_' or ('a' <= c and c <= 'z') {
		len = 0
		while ('0' <= c and c <= '9') or ('A' <= c and c <= 'Z') or c == '_' or ('a' <= c and c <= 'z')
			len ++
			c = peek(m_src, m_pos + len)
		wend
		m_type = token_type_ident
		m_val = strmid(m_src, m_pos, len)
		if (dict_has(m_reserved, m_val)) {
			m_type = dict_get(m_reserved, m_val)
			m_val = 0
		}
		m_pos += len
		return
	}
	if c == 0 {
		m_type = token_type_end
		m_val = 0
		return
	}
	m_val = 0
	m_pos ++
	m_type = c
	
	if c == '<' {
		switch peek(m_src, m_pos)
		case '<'
			m_type = token_type_lsh
			m_pos ++
			swbreak
		case '='
			m_type = token_type_lteq
			m_pos ++
		swend
	} else : if c == '>' {
		switch peek(m_src, m_pos)
		case '>'
			m_type = token_type_rsh
			m_pos ++
			swbreak
		case '='
			m_type = token_type_gteq
			m_pos ++
		swend
	} else : if c == '=' {
		if peek(m_src, m_pos) == '=' {
			m_type = token_type_eq
			m_pos ++
		}
	} else : if c == '!' {
		if peek(m_src, m_pos) == '=' {
			m_type = token_type_ne
			m_pos ++
		}
	} else : if c == '&' {
		if peek(m_src, m_pos) == '&' {
			m_type = token_type_and
			m_pos ++
		}
	} else : if c == '|' {
		if peek(m_src, m_pos) == '|' {
			m_type = token_type_or
			m_pos ++
		}
	}
	return
#define ctype accept(%1) parser_accept(thismod, %1)
#modcfunc parser_accept int type
	if m_type == type {
		gettoken
		return 1
	}
	return 0
#define expect(%1) parser_expect thismod, %1
#modfunc parser_expect int type
	if m_type == type {
		gettoken
	} else {
		m_type = token_type_error
	}
	return
#defcfunc token_type_to_op_type int token_type
	if token_type < 256 : return token_type
	switch token_type
#define ctype CT(%1) case token_type_%1: return optype_%1
	CT(eq)
	CT(ne)
	CT(lteq)
	CT(gteq)
	CT(and)
	CT(or)
	CT(lsh)
	CT(rsh)
#undef CT
	swend
	assert 0
#modcfunc parse_factor local val, local result
	if m_type == token_type_num {
		val = m_val
		gettoken
		return new_literal_node(val)
	}
	if m_type == token_type_ident {
		val = m_val
		gettoken
		if m_type == '=' {
			gettoken
			result = parse_expr(thismod)
			return new_assign_node(val, result)
		}
		return new_varref_node(val)
	}
	if accept('(') {
		result = parse_expr(thismod)
		expect ')'
		return result
	}
	m_type = token_type_error
	return null
#modcfunc parse_unary local op
	if m_type == '+' or m_type == '-' or m_type == '!' or m_type == '~' {
		op = token_type_to_op_type(m_type)
		gettoken
		return new_unaryop_node(op, parse_unary(thismod))
	} else {
		return parse_factor(thismod)
	}

#define lvars_parse_binop local op, local result
#define body_parse_binop(%1, %2) \
	result = %1(thismod) : \
	while %2 : \
		op = token_type_to_op_type(m_type) : \
		gettoken : \
		result = new_binaryop_node(op, result, %1(thismod)) : \
	wend : \
	return result

#modcfunc parse_muldiv lvars_parse_binop
	body_parse_binop parse_unary, m_type == '*' or m_type == '/' or m_type == '%'

#modcfunc parse_addsub lvars_parse_binop
	body_parse_binop parse_muldiv, m_type == '+' or m_type == '-'

#modcfunc parse_shift lvars_parse_binop
	body_parse_binop parse_addsub, m_type == token_type_lsh or m_type == token_type_rsh

#modcfunc parse_bitand lvars_parse_binop
	body_parse_binop parse_shift, m_type == '&'

#modcfunc parse_bitor lvars_parse_binop
	body_parse_binop parse_bitand, m_type == '|' or m_type == '^'

#defcfunc is_compare_op int token_type
	if token_type == token_type_eq : return 1
	if token_type == token_type_ne : return 1
	if token_type == '<' : return 1
	if token_type == '>' : return 1
	if token_type == token_type_lteq : return 1
	if token_type == token_type_gteq : return 1
	return 0
#modcfunc parse_compare lvars_parse_binop
	body_parse_binop parse_bitor, is_compare_op(m_type)

#modcfunc parse_and lvars_parse_binop
	body_parse_binop parse_compare, m_type == token_type_and

#modcfunc parse_or lvars_parse_binop
	body_parse_binop parse_and, m_type == token_type_or

#modcfunc parse_condop local result, local then_part, local else_part
	result = parse_or(thismod)
	if accept('?') {
		then_part = new_list_node_1(parse_expr(thismod))
		expect ':'
		else_part = new_list_node_1(parse_condop(thismod))
		result = new_if_node(result, then_part, else_part)
	}
	return result
#modcfunc parse_expr
	return parse_condop(thismod)
#modcfunc parse_if_stmt local cond, local then_part, local else_part
	expect '('
	cond = parse_expr(thismod)
	expect ')'
	then_part = parse_stmt(thismod)
	if accept(token_type_else) {
		else_part = parse_stmt(thismod)
	} else {
		else_part = new_list_node()
	}
	return new_if_node(cond, then_part, else_part)
#modcfunc parse_stmt local result
	if accept(';') {
		return null
	}
	if accept('{') {
		result = parse_stmt_list(thismod)
		expect '}'
		return result
	}
	if accept(token_type_if) {
		return parse_if_stmt(thismod)
	}
	result = parse_expr(thismod)
	expect ';'
	return result
#modcfunc parse_stmt_list local result, local stmt, local pos_bak, local type_bak
	result = new_list_node()
	repeat
		pos_bak = m_pos
		type_bak = m_type
		stmt = parse_stmt(thismod)
		if m_type == token_type_error and m_pos == pos_bak {
			m_type = type_bak
			break
		}
		if stmt != null : append_list_node result, stmt
	loop
	return result
#modcfunc parser_parse local result
	gettoken
	result = parse_stmt_list(thismod)
	if m_type != token_type_end {
		return null
	}
	return result
#defcfunc parse str src, local parser
	newmod parser, mod_parser, src
	return parser_parse(parser)
#global

#module mod_evaluator m_root, m_varmap
#modinit struct root
	m_root = root
	m_varmap = new_dict()
	return
#modcfunc eval_node struct node, local type, local result
	type = node_get_type(node)
	switch type
	case node_type_literal
		return node_get_val(node)
	case node_type_binaryop
		return eval_binaryop_node(thismod, node)
	case node_type_unaryop
		return eval_unaryop_node(thismod, node)
	case node_type_varref
		if dict_has(m_varmap, node_get_val(node)) == 0 {
			error "undefined variable: "+node_get_val(node)
		}
		return dict_get(m_varmap, node_get_val(node))
	case node_type_assign
		result = eval_node(thismod, node_get_child(node, 0))
		dict_put m_varmap, node_get_val(node), result
		return result
	case node_type_list
		result = 0
		repeat get_list_node_len(node)
			result = eval_node(thismod, node_get_child(node, cnt))
		loop
		return result
	case node_type_if
		return eval_if_node(thismod, node)
	default
		assert 0
	swend
#modcfunc eval_binaryop_node struct node, local op, local lhs, local rhs, local result
	op = node_get_val(node)
	lhs = node_get_lhs(node)
	rhs = node_get_rhs(node)
	switch op
#define CT(%1,%2) case %1: return eval_node(thismod, lhs) %2 eval_node(thismod, rhs)
	CT '+', +
	CT '-', -
	CT '*', *
	CT '/', /
	CT '%', \
	CT optype_lsh, <<
	CT optype_rsh, >>
	CT '&', &
	CT '|', |
	CT '^', ^
	CT optype_eq, ==
	CT optype_ne, !=
	CT '<', <
	CT '>', >
	CT optype_lteq, <=
	CT optype_gteq, >=
#undef CT
	case optype_and
		result = eval_node(thismod, lhs)
		if result == 0 : return result
		return eval_node(thismod, rhs)
	case optype_or
		result = eval_node(thismod, lhs)
		if result : return result
		return eval_node(thismod, rhs)
	swend
	assert 0
#modcfunc eval_unaryop_node struct node, local op, local lhs
	op = node_get_val(node)
	lhs = node_get_lhs(node)
	switch op
	case '+'
		return eval_node(thismod, lhs)
	case '-'
		return - eval_node(thismod, lhs)
	case '!'
		return eval_node(thismod, lhs) != 0
	case '~'
		return eval_node(thismod, lhs) ^ -1
	default
		assert 0
	swend
#modcfunc eval_if_node struct node
	cond = eval_node(thismod, get_if_node_cond(node))
	if cond {
		return eval_node(thismod, get_if_node_then(node))
	} else {
		return eval_node(thismod, get_if_node_else(node))
	}
#defcfunc evaluate struct root, local evaluator
	newmod evaluator, mod_evaluator, root
	return eval_node(evaluator, root)
#global

#module
#defcfunc isdigit int c
	return '0' <= c and c <= '9'
#defcfunc isspace int c
	if c == '\t' : return 1
	if c == 0x0a : return 1 // '\n'
	if c == 0x0b : return 1 // '\v'
	if c == 0x0c : return 1 // '\f'
	if c == '\r' : return 1
	if c == ' '  : return 1
	return 0
#deffunc error str msg
	dialog msg
	end
#global

	program = {"
		a = 42;
		if (a >= 100) {
			b = 1;
		} else if (a >= 0) {
			b = 2;
		} else {
			b = 3;
		}
	"}
	node = parse(program)
	if node == null {
		mes "syntax error"
	} else {
		mes inspect_node(node)
		mes "=> "+evaluate(node)
	}

インフォメーション

公開日時
2009年6月19日 午後10時2分14秒
最終更新日時
2009年6月19日 午後10時2分14秒
カテゴリ
HSP