mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
all: initial interpreter code (#12605)
This commit is contained in:
parent
525791fa3a
commit
2fbf7fea75
@ -123,7 +123,8 @@ fn main() {
|
||||
}
|
||||
else {}
|
||||
}
|
||||
if command in ['run', 'build', 'build-module'] || command.ends_with('.v') || os.exists(command) {
|
||||
if command in ['run', 'build', 'build-module', 'interpret'] || command.ends_with('.v')
|
||||
|| os.exists(command) {
|
||||
// println('command')
|
||||
// println(prefs.path)
|
||||
builder.compile(command, prefs)
|
||||
|
@ -1,18 +1,17 @@
|
||||
// This program displays the fibonacci sequence
|
||||
// import os
|
||||
import os
|
||||
|
||||
fn main() {
|
||||
// Check for user input
|
||||
// if os.args.len != 2 {
|
||||
// println('usage: fibonacci [rank]')
|
||||
if os.args.len != 2 {
|
||||
println('usage: fibonacci [rank]')
|
||||
|
||||
// Exit
|
||||
// return
|
||||
// }
|
||||
return
|
||||
}
|
||||
|
||||
// Parse first argument and cast it to int
|
||||
// stop := os.args[1].int()
|
||||
stop := 23
|
||||
|
||||
stop := os.args[1].int()
|
||||
// Can only calculate correctly until rank 92
|
||||
if stop > 92 {
|
||||
println('rank must be 92 or less')
|
||||
@ -20,9 +19,9 @@ fn main() {
|
||||
}
|
||||
|
||||
// Three consecutive terms of the sequence
|
||||
mut a := 0
|
||||
mut b := 0
|
||||
mut c := 1
|
||||
mut a := i64(0)
|
||||
mut b := i64(0)
|
||||
mut c := i64(1)
|
||||
println(a + c + c)
|
||||
for _ in 0 .. stop {
|
||||
// Set a and b to the next term
|
||||
|
@ -957,6 +957,7 @@ pub mut:
|
||||
key_type Type
|
||||
val_type Type
|
||||
cond_type Type
|
||||
high_type Type
|
||||
kind Kind // array/map/string
|
||||
label string // `label: for {`
|
||||
scope &Scope
|
||||
|
@ -719,11 +719,11 @@ fn (mut b Builder) cc_linux_cross() {
|
||||
verror(cc_res.output)
|
||||
return
|
||||
}
|
||||
mut linker_args := ['-L $sysroot/usr/lib/x86_64-linux-gnu/', '--sysroot=$sysroot', '-v',
|
||||
'-o $b.pref.out_name', '-m elf_x86_64',
|
||||
mut linker_args := ['-L$sysroot/usr/lib/x86_64-linux-gnu/', '-L$sysroot/lib/x86_64-linux-gnu',
|
||||
'--sysroot=$sysroot', '-v', '-o $b.pref.out_name', '-m elf_x86_64',
|
||||
'-dynamic-linker /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2',
|
||||
'$sysroot/crt1.o $sysroot/crti.o $obj_file', '-lc', '-lcrypto', '-lssl', '-lpthread',
|
||||
'$sysroot/crtn.o']
|
||||
'$sysroot/crtn.o', '-lm']
|
||||
linker_args << cflags.c_options_only_object_files()
|
||||
// -ldl
|
||||
b.dump_c_options(linker_args)
|
||||
|
@ -8,6 +8,8 @@ import os
|
||||
import rand
|
||||
import v.pref
|
||||
import v.util
|
||||
import v.eval
|
||||
import v.checker
|
||||
|
||||
fn (mut b Builder) get_vtmp_filename(base_file_name string, postfix string) string {
|
||||
vtmp := util.get_vtmp_folder()
|
||||
@ -42,6 +44,7 @@ pub fn compile(command string, pref &pref.Preferences) {
|
||||
.c { b.compile_c() }
|
||||
.js_node, .js_freestanding, .js_browser { b.compile_js() }
|
||||
.native { b.compile_native() }
|
||||
.interpret { b.interpret() }
|
||||
}
|
||||
mut timers := util.get_timers()
|
||||
timers.show_remaining()
|
||||
@ -116,7 +119,6 @@ fn (mut b Builder) run_compiled_executable_and_exit() {
|
||||
panic('Running iOS apps is not supported yet.')
|
||||
}
|
||||
if b.pref.is_verbose {
|
||||
println('============ running $b.pref.out_name ============')
|
||||
}
|
||||
if b.pref.is_test || b.pref.is_run {
|
||||
compiled_file := os.real_path(b.pref.out_name)
|
||||
@ -347,3 +349,15 @@ pub fn (v &Builder) get_user_files() []string {
|
||||
}
|
||||
return user_files
|
||||
}
|
||||
|
||||
pub fn (mut b Builder) interpret() {
|
||||
mut files := b.get_builtin_files()
|
||||
files << b.get_user_files()
|
||||
b.set_module_lookup_paths()
|
||||
b.front_and_middle_stages(files) or { return }
|
||||
|
||||
util.timing_start('INTERPRET')
|
||||
mut e := eval.new_eval(b.table, b.pref)
|
||||
e.eval(b.parsed_files)
|
||||
util.timing_measure('INTERPRET')
|
||||
}
|
||||
|
@ -234,7 +234,8 @@ fn (mut c Checker) check_shift(mut node ast.InfixExpr, left_type ast.Type, right
|
||||
// a negative value, the resulting value is implementation-defined (ID).
|
||||
left_sym_final := c.table.get_final_type_symbol(left_type)
|
||||
left_type_final := ast.Type(left_sym_final.idx)
|
||||
if node.op == .left_shift && left_type_final.is_signed() {
|
||||
if node.op == .left_shift && left_type_final.is_signed() && !(c.inside_unsafe
|
||||
&& c.file.path.contains('vlib/v/eval/infix.v')) {
|
||||
c.note('shifting a value from a signed type `$left_sym_final.name` can change the sign',
|
||||
node.left.position())
|
||||
}
|
||||
|
@ -28,7 +28,8 @@ const (
|
||||
valid_comptime_if_platforms = ['amd64', 'i386', 'aarch64', 'arm64', 'arm32', 'rv64', 'rv32']
|
||||
valid_comptime_if_cpu_features = ['x64', 'x32', 'little_endian', 'big_endian']
|
||||
valid_comptime_if_other = ['js', 'debug', 'prod', 'test', 'glibc', 'prealloc',
|
||||
'no_bounds_checking', 'freestanding', 'threads', 'js_node', 'js_browser', 'js_freestanding']
|
||||
'no_bounds_checking', 'freestanding', 'threads', 'js_node', 'js_browser', 'js_freestanding',
|
||||
'interpreter']
|
||||
valid_comptime_not_user_defined = all_valid_comptime_idents()
|
||||
array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice',
|
||||
'sort', 'contains', 'index', 'wait', 'any', 'all', 'first', 'last', 'pop']
|
||||
@ -4881,6 +4882,7 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
|
||||
} else {
|
||||
node.val_type = high_type
|
||||
}
|
||||
node.high_type = high_type
|
||||
node.scope.update_var_type(node.val_var, node.val_type)
|
||||
} else {
|
||||
sym := c.table.get_final_type_symbol(typ)
|
||||
@ -7208,6 +7210,7 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Position) bool {
|
||||
'prealloc' { return !c.pref.prealloc }
|
||||
'no_bounds_checking' { return cname !in c.pref.compile_defines_all }
|
||||
'freestanding' { return !c.pref.is_bare || c.pref.output_cross_c }
|
||||
'interpreter' { c.pref.backend != .interpret }
|
||||
else { return false }
|
||||
}
|
||||
} else if cname !in c.pref.compile_defines_all {
|
||||
|
@ -4,96 +4,198 @@
|
||||
module eval
|
||||
|
||||
import v.ast
|
||||
import v.checker
|
||||
import v.pref
|
||||
import v.util
|
||||
|
||||
pub type Object = int | string
|
||||
pub fn new_eval(table &ast.Table, pref &pref.Preferences) Eval {
|
||||
return Eval{
|
||||
table: table
|
||||
pref: pref
|
||||
}
|
||||
}
|
||||
|
||||
// const/global is `Object`
|
||||
type Symbol = Object | ast.EmptyStmt | ast.FnDecl
|
||||
|
||||
pub struct Eval {
|
||||
mut:
|
||||
checker checker.Checker
|
||||
vars map[string]Var
|
||||
table &ast.Table
|
||||
pref &pref.Preferences
|
||||
pub mut:
|
||||
table &ast.Table
|
||||
mods map[string]map[string]Symbol
|
||||
future_register_consts map[string]map[string]map[string]ast.ConstField // mod:file:name:field
|
||||
local_vars map[string]Var
|
||||
local_vars_stack []map[string]Var
|
||||
scope_idx int // this is increased when e.open_scope() is called, decreased when e.close_scope() (and all variables with that scope level deleted)
|
||||
returning bool
|
||||
return_values []Object
|
||||
cur_mod string
|
||||
cur_file string
|
||||
back_trace []string
|
||||
}
|
||||
|
||||
pub struct Var {
|
||||
value Object
|
||||
pub fn (mut e Eval) eval(files []&ast.File) {
|
||||
e.register_symbols(files)
|
||||
// println(files.map(it.path_base))
|
||||
e.run_func(e.mods['main']['main'] or { ast.EmptyStmt{} } as ast.FnDecl)
|
||||
}
|
||||
|
||||
pub fn (mut e Eval) eval(file ast.File, table &ast.Table) string {
|
||||
vpref := &pref.Preferences{}
|
||||
e.table = table
|
||||
mut res := ''
|
||||
e.checker = checker.new_checker(table, vpref)
|
||||
for stmt in file.stmts {
|
||||
res += e.stmt(stmt) + '\n'
|
||||
// first arg is reciever (if method)
|
||||
pub fn (mut e Eval) run_func(func ast.FnDecl, _args ...Object) {
|
||||
e.back_trace << func.name
|
||||
old_mod := e.cur_mod
|
||||
e.cur_mod = func.mod
|
||||
|
||||
old_file := e.cur_file
|
||||
e.cur_file = func.file
|
||||
defer {
|
||||
e.cur_mod = old_mod
|
||||
e.cur_file = old_file
|
||||
e.back_trace.pop()
|
||||
}
|
||||
return res.trim_space()
|
||||
}
|
||||
|
||||
fn print_object(o Object) {
|
||||
match o {
|
||||
int { println(o) }
|
||||
else { println('unknown object') }
|
||||
mut args := _args.clone()
|
||||
if func.params.len != args.len && !func.is_variadic {
|
||||
e.error('mismatched parameter length for $func.name: got `$args.len`, expected `$func.params.len`')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (o Object) str() string {
|
||||
match o {
|
||||
int { return o.str() }
|
||||
else { println('unknown object') }
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
fn (mut e Eval) stmt(node ast.Stmt) string {
|
||||
match node {
|
||||
ast.AssignStmt {
|
||||
// TODO; replaced VarDecl
|
||||
if func.name in ['print', 'println', 'eprint', 'eprintln'] {
|
||||
s := args[0].string() // stringify because println accepts anything as argument
|
||||
match func.name {
|
||||
'print' {
|
||||
print(s)
|
||||
}
|
||||
'println' {
|
||||
println(s)
|
||||
}
|
||||
'eprint' {
|
||||
eprint(s)
|
||||
}
|
||||
'eprintln' {
|
||||
eprintln(s)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
ast.ExprStmt {
|
||||
o := e.expr(node.expr)
|
||||
print('out: ')
|
||||
print_object(o)
|
||||
return o.str()
|
||||
}
|
||||
// ast.StructDecl {
|
||||
// println('s decl')
|
||||
// }
|
||||
// ast.VarDecl {
|
||||
// e.vars[it.name] = Var{
|
||||
// value: e.expr(it.expr)
|
||||
// }
|
||||
// }
|
||||
else {}
|
||||
}
|
||||
return '>>'
|
||||
}
|
||||
|
||||
fn (mut e Eval) expr(node ast.Expr) Object {
|
||||
match node {
|
||||
ast.IntegerLiteral {
|
||||
return node.val
|
||||
}
|
||||
ast.Ident {
|
||||
print_object(node.value)
|
||||
// Find the variable
|
||||
v := e.vars[node.name]
|
||||
return v.value
|
||||
}
|
||||
ast.InfixExpr {
|
||||
e.checker.infix_expr(mut node)
|
||||
// println('bin $it.op')
|
||||
left := e.expr(node.left) as int
|
||||
right := e.expr(node.right) as int
|
||||
match node.op {
|
||||
.plus { return left + right }
|
||||
.mul { return left * right }
|
||||
else {}
|
||||
} else {
|
||||
e.local_vars_stack << e.local_vars
|
||||
e.local_vars = {}
|
||||
old_scope := e.scope_idx
|
||||
e.scope_idx = 0
|
||||
e.open_scope()
|
||||
// have to do this because of cgen error
|
||||
args__ := if func.is_method { args[1..] } else { args }
|
||||
for i, arg in args__ {
|
||||
e.local_vars[(func.params[i]).name] = Var{
|
||||
val: arg
|
||||
scope_idx: e.scope_idx
|
||||
}
|
||||
}
|
||||
else {}
|
||||
if func.is_method {
|
||||
print(e.back_trace)
|
||||
println(func.receiver.typ - 65536)
|
||||
e.local_vars[func.receiver.name] = Var{
|
||||
val: args[0]
|
||||
scope_idx: e.scope_idx
|
||||
}
|
||||
}
|
||||
e.stmts(func.stmts)
|
||||
e.returning = false
|
||||
e.close_scope()
|
||||
e.scope_idx = old_scope
|
||||
e.local_vars = e.local_vars_stack.pop()
|
||||
}
|
||||
return 0
|
||||
// return Object{}
|
||||
}
|
||||
|
||||
pub fn (mut e Eval) register_symbols(files []&ast.File) {
|
||||
for file in files {
|
||||
// eprintln('registering file: $file.path_base')
|
||||
mod := file.mod.name
|
||||
e.register_symbol_stmts(file.stmts[1..], mod, file.path)
|
||||
|
||||
// eprintln('registered file: $file.path_base')
|
||||
}
|
||||
for mod, const_files in e.future_register_consts {
|
||||
e.cur_mod = mod
|
||||
|
||||
for file, fields in const_files {
|
||||
e.cur_file = file
|
||||
for _, field in fields {
|
||||
e.mods[mod][field.name.all_after_last('.')] = e.expr(field.expr, field.typ)
|
||||
if mod == 'os' && field.name.all_after_last('.') == 'args' {
|
||||
mut res := Array{}
|
||||
res.val << e.pref.out_name.all_after_last('/')
|
||||
for arg in e.pref.run_args {
|
||||
res.val << arg
|
||||
}
|
||||
e.mods[mod][field.name.all_after_last('.')] = Object(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut e Eval) register_symbol_stmts(stmts []ast.Stmt, mod string, file string) {
|
||||
for stmt in stmts { // first is just module declaration, so ignore
|
||||
e.register_symbol(stmt, mod, file)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut e Eval) register_symbol(stmt ast.Stmt, mod string, file string) {
|
||||
match stmt {
|
||||
ast.FnDecl {
|
||||
// this mess because c error
|
||||
x := ast.Stmt(stmt)
|
||||
y := Symbol(x as ast.FnDecl)
|
||||
e.mods[mod][stmt.name.all_after_last('.')] = y
|
||||
}
|
||||
ast.Import {} // already handled by builder, TODO: get `as` name
|
||||
ast.StructDecl {} // these are already parsed by the checker into e.table
|
||||
ast.InterfaceDecl {}
|
||||
ast.EnumDecl {}
|
||||
ast.TypeDecl {}
|
||||
ast.GlobalDecl {}
|
||||
ast.HashStmt {}
|
||||
ast.ConstDecl {
|
||||
// evaluate them later since they may use functions defined after this point
|
||||
for field in stmt.fields {
|
||||
e.future_register_consts[mod][file][field.name] = field
|
||||
}
|
||||
}
|
||||
ast.ExprStmt {
|
||||
println('expr')
|
||||
x := stmt.expr
|
||||
match x {
|
||||
ast.IfExpr {
|
||||
if !x.is_comptime {
|
||||
e.error('only comptime ifs are allowed in top level')
|
||||
}
|
||||
for i, branch in x.branches {
|
||||
mut do_if := false
|
||||
println('branch:$branch')
|
||||
match (branch.cond as ast.Ident).name {
|
||||
'windows' {
|
||||
do_if = e.pref.os == .windows
|
||||
}
|
||||
else {
|
||||
e.error('unknown compile time if')
|
||||
}
|
||||
}
|
||||
do_if = do_if || x.branches.len == i + 1
|
||||
if do_if {
|
||||
e.register_symbol_stmts(branch.stmts, mod, file)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown decleration expression statement $x.type_name()')
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unhandled decleration statement $stmt.type_name()')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (e Eval) error(msg string) {
|
||||
util.verror('interpreter', msg)
|
||||
}
|
||||
|
566
vlib/v/eval/expr.v
Normal file
566
vlib/v/eval/expr.v
Normal file
@ -0,0 +1,566 @@
|
||||
module eval
|
||||
|
||||
import v.ast
|
||||
import v.util
|
||||
import math
|
||||
import strconv
|
||||
|
||||
pub fn (mut e Eval) expr(expr ast.Expr, expecting ast.Type) Object {
|
||||
match expr {
|
||||
ast.CallExpr {
|
||||
// println(expr.is_method)
|
||||
// is_method := expr.left.type_name() != 'unknown v.ast.Expr'
|
||||
// println(is_method)
|
||||
if expr.name == 'int' {
|
||||
e.error('methods not supported')
|
||||
}
|
||||
mut args := expr.args.map(e.expr(it.expr, it.typ))
|
||||
if expr.is_method {
|
||||
args.prepend(e.expr(expr.left, expr.receiver_type))
|
||||
}
|
||||
match expr.language {
|
||||
.c {
|
||||
if expr.is_method {
|
||||
e.error('c does not have methods')
|
||||
}
|
||||
match expr.name.all_after('C.') {
|
||||
'write' {
|
||||
return Int{C.write(args[0].int_val(), args[1] as voidptr,
|
||||
args[2].int_val()), 32}
|
||||
}
|
||||
'calloc' {
|
||||
return Ptr{
|
||||
val: vcalloc(int(args[0].int_val() * args[1].int_val()))
|
||||
}
|
||||
}
|
||||
'getcwd' {
|
||||
unsafe {
|
||||
return Ptr{
|
||||
val: C.getcwd((args[0] as Ptr).val as voidptr, args[1].int_val())
|
||||
}
|
||||
}
|
||||
}
|
||||
'memcpy' {
|
||||
unsafe {
|
||||
return Ptr{
|
||||
val: C.memcpy(args[0] as voidptr, args[1] as voidptr,
|
||||
args[2].int_val())
|
||||
}
|
||||
}
|
||||
}
|
||||
// 'printf' {
|
||||
// mut voidptr_args := []voidptr{}
|
||||
// for arg in args[1..] {
|
||||
// // if arg is Int {
|
||||
// // voidptr_args << voidptr(arg.val)
|
||||
// // } else {
|
||||
// voidptr_args << voidptr(&arg)
|
||||
// // }
|
||||
// }
|
||||
// // println((e.local_vars['s'].val as string).str == voidptr_args[1])
|
||||
// println('helo?$voidptr_args')
|
||||
// // println((byteptr(voidptr_args[1])[0]))
|
||||
// x := strconv.v_sprintf(args[0] as string, ...voidptr_args)
|
||||
// // println('helo!')
|
||||
// // println(x.len)
|
||||
// y := C.write(1, x.str, x.len)
|
||||
// println('aft')
|
||||
// return Int{y, 32}
|
||||
// }
|
||||
else {
|
||||
e.error('unknown c function: `$expr.name`')
|
||||
}
|
||||
}
|
||||
}
|
||||
.v {
|
||||
// TODO: Anon functions
|
||||
name := expr.name.all_after_last('.')
|
||||
mod := expr.mod
|
||||
mut func := e.mods[mod][name] or {
|
||||
e.mods['builtin'][name] or { ast.EmptyStmt{} }
|
||||
}
|
||||
|
||||
if func is ast.FnDecl {
|
||||
e.run_func(func as ast.FnDecl, ...args)
|
||||
if e.return_values.len == 1 {
|
||||
return e.return_values[0]
|
||||
} else {
|
||||
return e.return_values
|
||||
}
|
||||
}
|
||||
e.error('unknown function: ${mod}.$name at line $expr.pos.line_nr')
|
||||
}
|
||||
// .js {
|
||||
// e.error('js is not supported')
|
||||
// }
|
||||
else {
|
||||
e.error('$expr.language is not supported as a call expression language')
|
||||
}
|
||||
}
|
||||
}
|
||||
ast.StringLiteral {
|
||||
// escape the escapes
|
||||
mut res := ''
|
||||
mut is_escape := false
|
||||
for c in expr.val {
|
||||
if is_escape {
|
||||
res += e.get_escape(rune(c)).str()
|
||||
} else if c == `\\` {
|
||||
is_escape = true
|
||||
} else {
|
||||
res += rune(c).str()
|
||||
}
|
||||
}
|
||||
|
||||
return Object(res)
|
||||
}
|
||||
ast.IfExpr {
|
||||
if expr.is_expr {
|
||||
e.error('`if` expressions not supported')
|
||||
}
|
||||
|
||||
if expr.is_comptime {
|
||||
for i, branch in expr.branches {
|
||||
mut do_if := false
|
||||
if expr.has_else && i + 1 == expr.branches.len { // else branch
|
||||
do_if = true
|
||||
} else {
|
||||
if branch.cond is ast.Ident {
|
||||
match branch.cond.name {
|
||||
'windows' {
|
||||
do_if = e.pref.os == .windows
|
||||
}
|
||||
'macos' {
|
||||
do_if = e.pref.os == .macos
|
||||
}
|
||||
'linux' {
|
||||
do_if = e.pref.os == .linux
|
||||
}
|
||||
'android' {
|
||||
do_if = e.pref.os == .android
|
||||
}
|
||||
'freebsd' {
|
||||
do_if = e.pref.os == .freebsd
|
||||
}
|
||||
'prealloc' {
|
||||
do_if = e.pref.prealloc
|
||||
}
|
||||
else {
|
||||
e.error('unknown compile time if: $branch.cond.name')
|
||||
}
|
||||
}
|
||||
} else if branch.cond is ast.PostfixExpr {
|
||||
do_if = (branch.cond.expr as ast.Ident).name in e.pref.compile_defines
|
||||
}
|
||||
}
|
||||
if do_if {
|
||||
e.stmts(branch.stmts)
|
||||
break
|
||||
}
|
||||
}
|
||||
return empty
|
||||
} else {
|
||||
for i, b in expr.branches {
|
||||
mut result := e.expr(b.cond, ast.bool_type_idx)
|
||||
|
||||
if expr.has_else && i + 1 == expr.branches.len { // else block
|
||||
e.stmts(b.stmts)
|
||||
break
|
||||
}
|
||||
if result is bool {
|
||||
if result as bool {
|
||||
e.stmts(b.stmts)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
e.error('non-bool expression: $b.cond')
|
||||
}
|
||||
}
|
||||
return empty
|
||||
}
|
||||
}
|
||||
ast.InfixExpr {
|
||||
left := e.expr(expr.left, expr.left_type)
|
||||
right := e.expr(expr.right, expr.right_type)
|
||||
return e.infix_expr(left, right, expr.op, expecting)
|
||||
}
|
||||
ast.IntegerLiteral {
|
||||
// return u64(strconv.parse_uint(expr.val, 0, 64)
|
||||
return i64(strconv.parse_int(expr.val, 0, 64) or {
|
||||
e.error('invalid integer literal: $expr.val')
|
||||
}) // TODO: numbers larger than 2^63 (for u64)
|
||||
}
|
||||
ast.FloatLiteral {
|
||||
return f64(strconv.atof64(expr.val))
|
||||
}
|
||||
ast.BoolLiteral {
|
||||
return expr.val
|
||||
}
|
||||
ast.Ident {
|
||||
match expr.kind {
|
||||
.variable {
|
||||
// println(e.local_vars[expr.name].val.type_name())
|
||||
return e.local_vars[expr.name].val
|
||||
}
|
||||
.constant {
|
||||
return if expr.name.contains('.') {
|
||||
e.mods[expr.name.all_before_last('.')]
|
||||
} else {
|
||||
e.mods[e.cur_mod]
|
||||
}[expr.name.all_after_last('.')] or { ast.EmptyStmt{} } as Object
|
||||
}
|
||||
else {
|
||||
e.error('unknown ident kind for `$expr.name`: $expr.kind')
|
||||
}
|
||||
}
|
||||
}
|
||||
ast.CastExpr {
|
||||
x := e.expr(expr.expr, expr.expr_type)
|
||||
if expr.typ in ast.signed_integer_type_idxs {
|
||||
match x {
|
||||
Uint {
|
||||
return Int{
|
||||
val: i64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
Int {
|
||||
return Int{
|
||||
val: i64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
Float {
|
||||
return Int{
|
||||
val: i64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
i64 {
|
||||
if expecting in ast.signed_integer_type_idxs {
|
||||
return Int{
|
||||
val: x
|
||||
size: i8(e.type_to_size(expecting))
|
||||
}
|
||||
} else {
|
||||
return Uint{
|
||||
val: u64(x)
|
||||
size: i8(e.type_to_size(expecting))
|
||||
}
|
||||
}
|
||||
}
|
||||
f64 {
|
||||
if expecting in ast.signed_integer_type_idxs {
|
||||
return Int{
|
||||
val: i64(x)
|
||||
size: i8(e.type_to_size(expecting))
|
||||
}
|
||||
} else {
|
||||
return Uint{
|
||||
val: u64(x)
|
||||
size: i8(e.type_to_size(expecting))
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown cast: ${e.table.get_type_symbol(expr.expr_type).str()} to ${e.table.get_type_symbol(expr.typ).str()}')
|
||||
}
|
||||
}
|
||||
} else if expr.typ in ast.unsigned_integer_type_idxs {
|
||||
match x {
|
||||
Uint {
|
||||
return Uint{
|
||||
val: u64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
Int {
|
||||
return Uint{
|
||||
val: u64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
Float {
|
||||
return Uint{
|
||||
val: u64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
i64 {
|
||||
return Uint{
|
||||
val: u64(x)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
f64 {
|
||||
if expecting in ast.signed_integer_type_idxs {
|
||||
return Int{
|
||||
val: i64(x)
|
||||
size: i8(e.type_to_size(expecting))
|
||||
}
|
||||
} else {
|
||||
return Uint{
|
||||
val: u64(x)
|
||||
size: i8(e.type_to_size(expecting))
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown cast: ${e.table.get_type_symbol(expr.expr_type).str()} to ${e.table.get_type_symbol(expr.typ).str()}')
|
||||
}
|
||||
}
|
||||
} else if expr.typ in ast.float_type_idxs {
|
||||
match x {
|
||||
Uint {
|
||||
return Float{
|
||||
val: f64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
Int {
|
||||
return Float{
|
||||
val: f64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
Float {
|
||||
return Float{
|
||||
val: f64(x.val)
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
f64 {
|
||||
return Float{
|
||||
val: x
|
||||
size: i8(e.type_to_size(expr.typ))
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown cast: ${e.table.get_type_symbol(expr.expr_type).str()} to ${e.table.get_type_symbol(expr.typ).str()}')
|
||||
}
|
||||
}
|
||||
} else if expr.typ in ast.pointer_type_idxs {
|
||||
y := *(x as Ptr).val
|
||||
if expr.typ == ast.byteptr_type_idx {
|
||||
match y {
|
||||
char, voidptr {
|
||||
unsafe {
|
||||
return Ptr{
|
||||
val: (x as Ptr).val
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown cast: ${e.table.get_type_symbol(expr.expr_type).str()} to ${e.table.get_type_symbol(expr.typ).str()}')
|
||||
}
|
||||
}
|
||||
} else if expr.typ == ast.voidptr_type_idx {
|
||||
match y {
|
||||
char, Int {
|
||||
unsafe {
|
||||
return Object(voidptr((x as Ptr).val))
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown cast: ${e.table.get_type_symbol(expr.expr_type).str()} to ${e.table.get_type_symbol(expr.typ).str()}')
|
||||
}
|
||||
}
|
||||
} else if expr.typ == ast.charptr_type_idx {
|
||||
match y {
|
||||
voidptr, Int {
|
||||
unsafe {
|
||||
return Ptr{
|
||||
val: &char((x as Ptr).val)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown cast: ${e.table.get_type_symbol(expr.expr_type).str()} to ${e.table.get_type_symbol(expr.typ).str()}')
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if e.table.get_type_symbol(expr.typ).kind in [.interface_, .sum_type] {
|
||||
eprintln(util.formatted_error('warning:', 'sumtype or interface casts return void currently',
|
||||
e.cur_file, expr.pos))
|
||||
} else {
|
||||
e.error('unknown cast: ${e.table.get_type_symbol(expr.expr_type).str()} to ${e.table.get_type_symbol(expr.typ).str()}')
|
||||
}
|
||||
}
|
||||
ast.SelectorExpr {
|
||||
exp := e.expr(expr.expr, expr.expr_type)
|
||||
match exp {
|
||||
string {
|
||||
match expr.field_name {
|
||||
'str' {
|
||||
return Ptr{
|
||||
val: exp.str
|
||||
}
|
||||
}
|
||||
'len' {
|
||||
return Int{exp.len, 32}
|
||||
}
|
||||
else {
|
||||
e.error('unknown selector to string: $expr.field_name')
|
||||
}
|
||||
}
|
||||
}
|
||||
Array {
|
||||
match expr.field_name {
|
||||
'len' {
|
||||
return Int{exp.val.len, 32}
|
||||
}
|
||||
else {
|
||||
e.error('unknown selector to array: $expr.field_name')
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown selector expression: $exp.type_name()')
|
||||
}
|
||||
}
|
||||
e.error(exp.str())
|
||||
}
|
||||
ast.ArrayInit {
|
||||
if expr.has_len || expr.has_cap || expr.has_default {
|
||||
if expr.has_len && !expr.has_cap && expr.has_default {
|
||||
return Array{
|
||||
val: []Object{len: int((e.expr(expr.len_expr, 7) as Int).val), init: e.expr(expr.default_expr,
|
||||
expr.elem_type)}
|
||||
}
|
||||
} else if !expr.has_len && expr.has_cap && !expr.has_default {
|
||||
return Array{
|
||||
val: []Object{cap: int((e.expr(expr.cap_expr, 7) as Int).val)}
|
||||
}
|
||||
} else if !expr.has_len && !expr.has_cap && !expr.has_default {
|
||||
return Array{
|
||||
val: []Object{}
|
||||
}
|
||||
} else {
|
||||
e.error('unknown array init combination; len: $expr.has_len, cap: $expr.has_cap, init: $expr.has_default')
|
||||
}
|
||||
}
|
||||
if expr.is_fixed || expr.has_val {
|
||||
e.error('fixed arrays are not supported')
|
||||
}
|
||||
mut res := Array{
|
||||
val: []Object{cap: expr.exprs.len}
|
||||
}
|
||||
|
||||
for i, exp in expr.exprs {
|
||||
res.val << e.expr(exp, expr.expr_types[i])
|
||||
}
|
||||
return res
|
||||
}
|
||||
ast.CharLiteral {
|
||||
if expr.val.len !in [1, 2] {
|
||||
e.error('invalid size of char literal: $expr.val.len')
|
||||
}
|
||||
if expr.val[0] == `\\` { // is an escape
|
||||
return e.get_escape(rune(expr.val[1]))
|
||||
} else {
|
||||
return rune(expr.val[0])
|
||||
}
|
||||
}
|
||||
ast.StructInit {
|
||||
// eprintln('unhandled struct init at line $expr.pos.line_nr')
|
||||
return 'helo'
|
||||
}
|
||||
ast.SizeOf {
|
||||
return Uint{e.type_to_size(expr.typ), 64}
|
||||
}
|
||||
ast.ParExpr {
|
||||
return e.expr(expr.expr, expecting)
|
||||
}
|
||||
ast.PrefixExpr {
|
||||
match expr.op {
|
||||
.amp {
|
||||
x := e.expr(expr.right, expr.right_type)
|
||||
return Ptr{
|
||||
val: &x
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unhandled prefix expression $expr.op')
|
||||
}
|
||||
}
|
||||
}
|
||||
ast.PostfixExpr {
|
||||
match expr.op {
|
||||
.inc {
|
||||
e.add(expr.expr, Int{1, 64})
|
||||
return e.expr(expr.expr, ast.i64_type_idx)
|
||||
}
|
||||
.dec {
|
||||
e.add(expr.expr, Int{-1, 64})
|
||||
return e.expr(expr.expr, ast.i64_type_idx)
|
||||
}
|
||||
else {
|
||||
e.error('unhandled postfix expression $expr.op')
|
||||
}
|
||||
}
|
||||
}
|
||||
ast.StringInterLiteral {
|
||||
mut res := expr.vals[0]
|
||||
|
||||
for i, exp in expr.exprs {
|
||||
res += e.expr(exp, expr.expr_types[i]).string()
|
||||
res += expr.vals[i + 1]
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
else {
|
||||
e.error('unhandled expression $expr.type_name()')
|
||||
}
|
||||
}
|
||||
return empty
|
||||
}
|
||||
|
||||
fn (e Eval) type_to_size(typ ast.Type) u64 {
|
||||
match typ {
|
||||
ast.voidptr_type_idx, ast.byteptr_type_idx, ast.charptr_type_idx {
|
||||
return u64(if e.pref.m64 {
|
||||
64
|
||||
} else {
|
||||
32
|
||||
})
|
||||
}
|
||||
ast.i8_type_idx, ast.i16_type_idx, ast.int_type_idx, ast.i64_type_idx {
|
||||
return u64(math.exp2(f64(typ - 2))) // this formula converts the type number to the bitsize
|
||||
}
|
||||
ast.byte_type_idx, ast.u16_type_idx, ast.u32_type_idx, ast.u64_type_idx {
|
||||
return u64(math.exp2(f64(typ - 6))) // this formula converts the type number to the bitsize
|
||||
}
|
||||
ast.int_literal_type_idx, ast.float_literal_type_idx {
|
||||
return 64
|
||||
}
|
||||
ast.f32_type_idx, ast.f64_type_idx {
|
||||
return u64(math.exp2(f64(typ - 8))) // this formula converts the type number to the bitsize
|
||||
}
|
||||
else {
|
||||
e.error('type_to_size(): unknown type: ${e.table.get_type_symbol(typ).str()}')
|
||||
return -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (e Eval) get_escape(r rune) rune {
|
||||
res := match r {
|
||||
`\\` {
|
||||
`\\`
|
||||
}
|
||||
`n` {
|
||||
`\n`
|
||||
}
|
||||
`0` {
|
||||
`\0`
|
||||
}
|
||||
else {
|
||||
`e`
|
||||
}
|
||||
}
|
||||
if res == `e` {
|
||||
e.error('unknown escape: `$r`')
|
||||
}
|
||||
return res
|
||||
}
|
141
vlib/v/eval/gen/infix_gen.v
Normal file
141
vlib/v/eval/gen/infix_gen.v
Normal file
@ -0,0 +1,141 @@
|
||||
// this file generates ../infix.v
|
||||
module main
|
||||
|
||||
import strings
|
||||
import os
|
||||
|
||||
const (
|
||||
header = 'module eval
|
||||
import v.token
|
||||
import v.ast
|
||||
fn(e Eval)infix_expr(left Object,right Object,op token.Kind,expecting ast.Type)Object{match op{'
|
||||
footer = "else{e.error('unknown infix expression: \$op')}}return empty // should e.error before this anyway
|
||||
}
|
||||
"
|
||||
uk_expect_footer = "else{e.error('unknown infix expectation: \${e.table.get_type_symbol(expecting).str()}')}}"
|
||||
comparison = {
|
||||
'gt': '>'
|
||||
'lt': '<'
|
||||
'eq': '=='
|
||||
'ne': '!='
|
||||
}
|
||||
math_ops = {
|
||||
'plus': '+'
|
||||
'minus': '*'
|
||||
'mul': '+'
|
||||
'div': '+'
|
||||
'right_shift': '>>'
|
||||
'left_shift': '<<'
|
||||
}
|
||||
compound_types = ['Int', 'Uint', 'Float']
|
||||
literal_types = ['i64', 'f64']
|
||||
)
|
||||
|
||||
fn main() {
|
||||
mut b := strings.new_builder(124000)
|
||||
b.write_string(header)
|
||||
|
||||
for enm, op in comparison {
|
||||
b.write_string('.$enm{match left{')
|
||||
for ct in compound_types {
|
||||
b.write_string('$ct {match right{')
|
||||
for ct2 in compound_types {
|
||||
b.write_string('$ct2{return left.val${op}right.val}')
|
||||
}
|
||||
for lt2 in literal_types {
|
||||
b.write_string('$lt2{return left.val${op}right}')
|
||||
}
|
||||
b.write_string("else{e.error('invalid operands to $op: $ct and \$right.type_name()')}}}")
|
||||
}
|
||||
for lt in literal_types {
|
||||
b.write_string('$lt {match right{')
|
||||
for ct2 in compound_types {
|
||||
b.write_string('$ct2{return left${op}right.val}')
|
||||
}
|
||||
for lt2 in literal_types {
|
||||
b.write_string('$lt2{return left${op}right}')
|
||||
}
|
||||
b.write_string("else {e.error('invalid operands to $op: ")
|
||||
b.write_string(if lt == 'i64' { 'int' } else { 'float' })
|
||||
b.write_string(" literal and \$right.type_name()')}}}")
|
||||
}
|
||||
if op in ['==', '!='] {
|
||||
b.write_string('string{match right{string{return left${op}right}else{e.error(\'invalid operands to $op: string and \$right.type_name()\')}}}')
|
||||
}
|
||||
b.write_string("else {e.error('invalid operands to $op: \$left.type_name() and \$right.type_name()')}}}")
|
||||
}
|
||||
for math, op in math_ops {
|
||||
b.write_string('.$math{match left{')
|
||||
for ct in compound_types {
|
||||
if op in ['<<', '>>'] && ct == 'Float' {
|
||||
continue
|
||||
}
|
||||
b.write_string('$ct {match right{')
|
||||
for ct2 in compound_types {
|
||||
if op in ['<<', '>>'] && ct2 == 'Float' {
|
||||
continue
|
||||
}
|
||||
unsafe_start, unsafe_end := if op in ['<<', '>>'] { 'unsafe{', '}' } else { '', '' }
|
||||
b.write_string('$ct2{if expecting in ast.signed_integer_type_idxs{return Int{i64(left.val)+i64(right.val),i8(e.type_to_size(expecting))}}else if expecting in ast.unsigned_integer_type_idxs{return Uint{u64(left.val)+u64(right.val),i8(e.type_to_size(expecting))}}else if expecting==ast.int_literal_type_idx{${unsafe_start}return i64(i64(left.val)${op}i64(right.val))$unsafe_end}')
|
||||
if op !in ['<<', '>>'] {
|
||||
b.write_string('else if expecting in ast.float_type_idxs{return Float{f64(left.val)${op}f64(right.val), i8(e.type_to_size(expecting))}}else if expecting==ast.float_literal_type_idx{return f64(f64(left.val)${op}f64(right.val))}')
|
||||
}
|
||||
b.write_string(uk_expect_footer)
|
||||
}
|
||||
for lt2 in literal_types {
|
||||
if op in ['<<', '>>'] && lt2 == 'f64' {
|
||||
continue
|
||||
}
|
||||
unsafe_start, unsafe_end := if op in ['<<', '>>'] { 'unsafe{', '}' } else { '', '' }
|
||||
b.write_string('$lt2{if expecting in ast.signed_integer_type_idxs{return Int{i64(left.val)+i64(right),i8(e.type_to_size(expecting))}}else if expecting in ast.unsigned_integer_type_idxs{return Uint{u64(left.val)+u64(right),i8(e.type_to_size(expecting))}}else if expecting==ast.int_literal_type_idx{${unsafe_start}return i64(i64(left.val)${op}i64(right))$unsafe_end}')
|
||||
if op !in ['<<', '>>'] {
|
||||
b.write_string('else if expecting in ast.float_type_idxs{return Float{f64(left.val)${op}f64(right), i8(e.type_to_size(expecting))}}else if expecting==ast.float_literal_type_idx{return f64(f64(left.val)${op}f64(right))}')
|
||||
}
|
||||
b.write_string(uk_expect_footer)
|
||||
}
|
||||
b.write_string("else {e.error('invalid operands to $op: $ct and \$right.type_name()')}}}")
|
||||
}
|
||||
for lt in literal_types {
|
||||
if op in ['<<', '>>'] && lt == 'f64' {
|
||||
continue
|
||||
}
|
||||
b.write_string('$lt{match right{')
|
||||
for ct2 in compound_types {
|
||||
if op in ['<<', '>>'] && ct2 == 'Float' {
|
||||
continue
|
||||
}
|
||||
unsafe_start, unsafe_end := if op in ['<<', '>>'] { 'unsafe{', '}' } else { '', '' }
|
||||
b.write_string('$ct2{if expecting in ast.signed_integer_type_idxs{return Int{i64(left)+i64(right.val),i8(e.type_to_size(expecting))}}else if expecting in ast.unsigned_integer_type_idxs{return Uint{u64(left)+u64(right.val),i8(e.type_to_size(expecting))}}else if expecting==ast.int_literal_type_idx{${unsafe_start}return i64(i64(left)${op}i64(right.val))$unsafe_end}')
|
||||
if op !in ['<<', '>>'] {
|
||||
b.write_string('else if expecting in ast.float_type_idxs{return Float{f64(left)${op}f64(right.val), i8(e.type_to_size(expecting))}}else if expecting==ast.float_literal_type_idx{return f64(f64(left)${op}f64(right.val))}')
|
||||
}
|
||||
b.write_string(uk_expect_footer)
|
||||
}
|
||||
for lt2 in literal_types {
|
||||
if op in ['<<', '>>'] && lt2 == 'f64' {
|
||||
continue
|
||||
}
|
||||
unsafe_start, unsafe_end := if op in ['<<', '>>'] { 'unsafe{', '}' } else { '', '' }
|
||||
b.write_string('$lt2{if expecting in ast.signed_integer_type_idxs{return Int{i64(left)+i64(right),i8(e.type_to_size(expecting))}}else if expecting in ast.unsigned_integer_type_idxs{return Uint{u64(left)+u64(right),i8(e.type_to_size(expecting))}}else if expecting==ast.int_literal_type_idx{${unsafe_start}return i64(i64(left)${op}i64(right))$unsafe_end}')
|
||||
if op !in ['<<', '>>'] {
|
||||
b.write_string('else if expecting in ast.float_type_idxs{return Float{f64(left)${op}f64(right), i8(e.type_to_size(expecting))}}else if expecting==ast.float_literal_type_idx{return f64(f64(left)${op}f64(right))}')
|
||||
}
|
||||
b.write_string(uk_expect_footer)
|
||||
}
|
||||
b.write_string("else {e.error('invalid operands to $op: ")
|
||||
b.write_string(if lt == 'i64' { 'int' } else { 'float' })
|
||||
b.write_string(" literal and \$right.type_name()')}}}")
|
||||
}
|
||||
b.write_string("else {e.error('invalid operands to $op: \$left.type_name() and \$right.type_name()')}}}")
|
||||
}
|
||||
|
||||
b.write_string(footer)
|
||||
|
||||
path := @FILE.all_before(@FILE.all_after_last('/')) + '../infix.v'
|
||||
os.write_file(path, b.str()) or { panic(err) }
|
||||
res := os.execute(@VEXE + ' fmt -w ' + path)
|
||||
if res.exit_code != 0 {
|
||||
eprintln('v fmt failed!')
|
||||
panic(res.output)
|
||||
}
|
||||
}
|
2211
vlib/v/eval/infix.v
Normal file
2211
vlib/v/eval/infix.v
Normal file
File diff suppressed because it is too large
Load Diff
150
vlib/v/eval/object.v
Normal file
150
vlib/v/eval/object.v
Normal file
@ -0,0 +1,150 @@
|
||||
module eval
|
||||
|
||||
const empty = Void{}
|
||||
|
||||
// NB: i64 is an int_literal, NOT an i64 (same with f64)
|
||||
type Object = Array
|
||||
| Float
|
||||
| Int
|
||||
| Ptr
|
||||
| Uint
|
||||
| Void
|
||||
| []Object
|
||||
| bool
|
||||
| byte
|
||||
| char
|
||||
| f64
|
||||
| i64
|
||||
| rune
|
||||
| string
|
||||
| voidptr
|
||||
|
||||
// string is the same as the autogenerated str() methods
|
||||
pub fn (o Object) string() string {
|
||||
match o {
|
||||
byte {
|
||||
panic('error: byte should only be used for &byte')
|
||||
}
|
||||
bool {
|
||||
return o.str()
|
||||
}
|
||||
i64 { // int_literal
|
||||
return o.str()
|
||||
}
|
||||
f64 { // float_literal
|
||||
return o.str()
|
||||
}
|
||||
Int {
|
||||
return o.val.str()
|
||||
}
|
||||
Uint {
|
||||
return o.val.str()
|
||||
}
|
||||
Float {
|
||||
return o.val.str()
|
||||
}
|
||||
string {
|
||||
return o
|
||||
}
|
||||
Void {
|
||||
return ''
|
||||
}
|
||||
[]Object {
|
||||
mut res := '('
|
||||
for i, obj in o {
|
||||
res += obj.str()
|
||||
if i + 1 != o.len {
|
||||
res += ', '
|
||||
}
|
||||
}
|
||||
return res + ')'
|
||||
}
|
||||
voidptr {
|
||||
return o.str()
|
||||
}
|
||||
char {
|
||||
return int(o).str()
|
||||
}
|
||||
rune {
|
||||
return o.str()
|
||||
}
|
||||
Array {
|
||||
mut res := '['
|
||||
for i, val in o.val {
|
||||
res += val.string()
|
||||
if i + 1 != o.val.len {
|
||||
res += ', '
|
||||
}
|
||||
}
|
||||
return res + ']'
|
||||
}
|
||||
Ptr {
|
||||
return o.val.str()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (o Object) int_val() i64 {
|
||||
match o {
|
||||
Int {
|
||||
return o.val
|
||||
}
|
||||
Uint {
|
||||
return i64(o.val)
|
||||
}
|
||||
i64 {
|
||||
return o
|
||||
}
|
||||
else {
|
||||
panic('Object.int_val(): not an int')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (o Object) float_val() f64 {
|
||||
match o {
|
||||
Float {
|
||||
return o.val
|
||||
}
|
||||
f64 {
|
||||
return o
|
||||
}
|
||||
else {
|
||||
panic('Object.float_val(): not a float')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Void {}
|
||||
|
||||
pub struct Int {
|
||||
pub mut:
|
||||
val i64
|
||||
size i8 // 8/16/32/64
|
||||
}
|
||||
|
||||
pub struct Uint {
|
||||
pub mut:
|
||||
val u64
|
||||
size i8 // 8/16/32/64
|
||||
}
|
||||
|
||||
pub struct Float {
|
||||
pub mut:
|
||||
val f64
|
||||
size i8 // 8/16/32/64
|
||||
}
|
||||
|
||||
pub struct Array {
|
||||
pub mut:
|
||||
val []Object
|
||||
}
|
||||
|
||||
pub struct Ptr {
|
||||
val &Object
|
||||
}
|
||||
|
||||
// override the autogenerated str, since it does not work
|
||||
fn (p Ptr) str() string {
|
||||
return u64(p.val).str()
|
||||
}
|
93
vlib/v/eval/stmt.v
Normal file
93
vlib/v/eval/stmt.v
Normal file
@ -0,0 +1,93 @@
|
||||
module eval
|
||||
|
||||
import v.ast
|
||||
|
||||
pub fn (mut e Eval) stmts(stmts []ast.Stmt) {
|
||||
e.open_scope()
|
||||
for stmt in stmts {
|
||||
e.stmt(stmt)
|
||||
if e.returning {
|
||||
break
|
||||
}
|
||||
}
|
||||
e.close_scope()
|
||||
}
|
||||
|
||||
pub fn (mut e Eval) stmt(stmt ast.Stmt) {
|
||||
match stmt {
|
||||
ast.ExprStmt {
|
||||
e.expr(stmt.expr, stmt.typ)
|
||||
}
|
||||
ast.AssignStmt {
|
||||
// if stmt.left.len != 1 {
|
||||
// e.error('multiple values assignments are not supported')
|
||||
// }
|
||||
mut rights := []Object{}
|
||||
for i, right in stmt.right {
|
||||
rights << e.expr(right, stmt.right_types[i])
|
||||
}
|
||||
if rights[0] is []Object { // needs to be unpacked
|
||||
e.error('multiple assignment from function is not supported')
|
||||
}
|
||||
match stmt.op {
|
||||
.decl_assign {
|
||||
for i, left in stmt.left {
|
||||
e.set(left, rights[i], true, stmt.left_types[i])
|
||||
}
|
||||
}
|
||||
.assign {
|
||||
for i, left in stmt.left {
|
||||
e.set(left, rights[i], false, stmt.left_types[i])
|
||||
}
|
||||
}
|
||||
else {
|
||||
e.error('unknown assign statment: $stmt.op')
|
||||
}
|
||||
}
|
||||
}
|
||||
ast.Return {
|
||||
e.returning = true
|
||||
e.return_values = []
|
||||
for i, expr in stmt.exprs {
|
||||
e.return_values << e.expr(expr, stmt.types[i])
|
||||
}
|
||||
}
|
||||
ast.ForInStmt {
|
||||
if !stmt.is_range {
|
||||
e.error('only range for in statements are supported')
|
||||
}
|
||||
if stmt.key_var != '' {
|
||||
e.error('keys are not supported in for in statements')
|
||||
}
|
||||
e.open_scope()
|
||||
underscore := stmt.val_var == '_'
|
||||
if !underscore {
|
||||
e.set(ast.Ident{ name: stmt.val_var, scope: 0 }, Int{-1, 32}, true, stmt.val_type)
|
||||
}
|
||||
for i in (e.expr(stmt.cond, ast.int_type_idx) as Int).val .. (e.expr(stmt.high,
|
||||
ast.int_type_idx) as Int).val {
|
||||
if !underscore {
|
||||
e.set(ast.Ident{ name: stmt.val_var, scope: 0 }, Int{i, 32}, false,
|
||||
stmt.val_type)
|
||||
}
|
||||
e.stmts(stmt.stmts)
|
||||
}
|
||||
e.close_scope()
|
||||
}
|
||||
ast.ForStmt {
|
||||
for {
|
||||
should_break := e.expr(stmt.cond, ast.bool_type_idx)
|
||||
if !(should_break as bool) {
|
||||
break
|
||||
}
|
||||
e.stmts(stmt.stmts)
|
||||
}
|
||||
}
|
||||
ast.Block {
|
||||
e.stmts(stmt.stmts)
|
||||
}
|
||||
else {
|
||||
e.error('unhandled statement $stmt.type_name()')
|
||||
}
|
||||
}
|
||||
}
|
68
vlib/v/eval/var.v
Normal file
68
vlib/v/eval/var.v
Normal file
@ -0,0 +1,68 @@
|
||||
module eval
|
||||
|
||||
import v.ast
|
||||
|
||||
pub struct Var {
|
||||
pub mut:
|
||||
val Object
|
||||
pub:
|
||||
scope_idx int
|
||||
typ ast.Type
|
||||
}
|
||||
|
||||
pub fn (mut e Eval) open_scope() {
|
||||
e.scope_idx++
|
||||
}
|
||||
|
||||
pub fn (mut e Eval) close_scope() {
|
||||
e.scope_idx--
|
||||
for name, var in e.local_vars {
|
||||
if var.scope_idx > e.scope_idx {
|
||||
e.local_vars.delete(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut e Eval) set(expr ast.Expr, val Object, init bool, typ ast.Type) {
|
||||
match expr {
|
||||
ast.Ident {
|
||||
if init {
|
||||
e.local_vars[expr.name] = Var{
|
||||
val: val
|
||||
scope_idx: e.scope_idx
|
||||
typ: typ
|
||||
}
|
||||
} else {
|
||||
e.local_vars[expr.name].val = val
|
||||
}
|
||||
}
|
||||
ast.IndexExpr {
|
||||
panic('>>$expr.pos, $e.cur_file')
|
||||
|
||||
// if init {
|
||||
// e.error('index init assignment')
|
||||
// } else {
|
||||
// mut x := (e.local_vars[(expr.left as ast.Ident).name].val)
|
||||
// if x is Array {
|
||||
// x.val[(e.expr(expr.index, ast.int_type_idx) as Int).val] = val
|
||||
// }
|
||||
// }
|
||||
}
|
||||
else {
|
||||
panic('unknown left value to assign statment: $expr.type_name()')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// val and expr must be both numeric types, or both string
|
||||
pub fn (mut e Eval) add(expr ast.Expr, val Object) {
|
||||
match expr {
|
||||
ast.Ident {
|
||||
e.local_vars[expr.name].val = e.infix_expr(e.local_vars[expr.name].val, val,
|
||||
.plus, e.local_vars[expr.name].typ)
|
||||
}
|
||||
else {
|
||||
panic('unknown left value to add statment: $expr.type_name()')
|
||||
}
|
||||
}
|
||||
}
|
@ -49,17 +49,15 @@ pub enum Backend {
|
||||
js_browser // The JavaScript browser backend
|
||||
js_freestanding // The JavaScript freestanding backend
|
||||
native // The Native backend
|
||||
interpret // Interpret the ast
|
||||
}
|
||||
|
||||
pub fn (b Backend) is_js() bool {
|
||||
match b {
|
||||
.js_node, .js_browser, .js_freestanding {
|
||||
return true
|
||||
}
|
||||
else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return b in [
|
||||
.js_node,
|
||||
.js_browser,
|
||||
.js_freestanding,
|
||||
]
|
||||
}
|
||||
|
||||
pub enum CompilerType {
|
||||
@ -486,6 +484,9 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences
|
||||
res.backend = .native
|
||||
res.build_options << arg
|
||||
}
|
||||
'-interpret' {
|
||||
res.backend = .interpret
|
||||
}
|
||||
'-W' {
|
||||
res.warns_are_errors = true
|
||||
}
|
||||
@ -707,6 +708,21 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences
|
||||
res.is_run = true
|
||||
res.path = command
|
||||
res.run_args = args[command_pos + 1..]
|
||||
} else if command == 'interpret' {
|
||||
res.backend = .interpret
|
||||
if command_pos + 2 > args.len {
|
||||
eprintln('v interpret: no v files listed')
|
||||
exit(1)
|
||||
}
|
||||
res.path = args[command_pos + 1]
|
||||
res.run_args = args[command_pos + 2..]
|
||||
|
||||
must_exist(res.path)
|
||||
if !res.path.ends_with('.v') && os.is_executable(res.path) && os.is_file(res.path)
|
||||
&& os.is_file(res.path + '.v') {
|
||||
eprintln('It looks like you wanted to run "${res.path}.v", so we went ahead and did that since "$res.path" is an executable.')
|
||||
res.path += '.v'
|
||||
}
|
||||
}
|
||||
if command == 'build-module' {
|
||||
res.build_mode = .build_module
|
||||
@ -801,6 +817,7 @@ pub fn backend_from_string(s string) ?Backend {
|
||||
'js_browser' { return .js_browser }
|
||||
'js_freestanding' { return .js_freestanding }
|
||||
'native' { return .native }
|
||||
'interpret' { return .interpret }
|
||||
else { return error('Unknown backend type $s') }
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ pub fn (prefs &Preferences) should_compile_filtered_files(dir string, files_ []s
|
||||
|| file.all_before_last('.v').all_before_last('.').ends_with('_test') {
|
||||
continue
|
||||
}
|
||||
if prefs.backend == .c && !prefs.should_compile_c(file) {
|
||||
if prefs.backend in [.c, .interpret] && !prefs.should_compile_c(file) {
|
||||
continue
|
||||
}
|
||||
if prefs.backend.is_js() && !prefs.should_compile_js(file) {
|
||||
|
Loading…
Reference in New Issue
Block a user