all: mutability check (part 1); enable mutable sumtype args

pull/6449/head
Alexander Medvednikov 2020-09-22 05:28:29 +02:00
parent 1ee0939f69
commit 624f22e27e
12 changed files with 122 additions and 125 deletions

View File

@ -145,7 +145,7 @@ pub fn (a array) repeat(count int) array {
// array.sort sorts array in-place using given `compare` function as comparator // array.sort sorts array in-place using given `compare` function as comparator
pub fn (mut a array) sort_with_compare(compare voidptr) { pub fn (mut a array) sort_with_compare(compare voidptr) {
C.qsort(a.data, a.len, a.element_size, compare) C.qsort(mut a.data, a.len, a.element_size, compare)
} }
// array.insert // array.insert

View File

@ -274,7 +274,7 @@ fn f64_to_decimal(mant u64, exp u64) Dec64 {
e10 = int(q) + e2 e10 = int(q) + e2
i := -e2 - int(q) i := -e2 - int(q)
k := pow5_bits(i) - pow5_num_bits_64 k := pow5_bits(i) - pow5_num_bits_64
mut j := int(q) - k j := int(q) - k
mul := pow5_split_64[i] mul := pow5_split_64[i]
vr = mul_shift_64(u64(4) * m2 , mul, j) vr = mul_shift_64(u64(4) * m2 , mul, j)
vp = mul_shift_64(u64(4) * m2 + u64(2) , mul, j) vp = mul_shift_64(u64(4) * m2 + u64(2) , mul, j)

View File

@ -777,7 +777,7 @@ pub fn v_sprintf(str string, pt ... voidptr) string{
if ch in [`f`, `F`] { if ch in [`f`, `F`] {
v_sprintf_panic(p_index, pt.len) v_sprintf_panic(p_index, pt.len)
x := unsafe {*(&f64(pt[p_index]))} x := unsafe {*(&f64(pt[p_index]))}
mut positive := x >= f64(0.0) positive := x >= f64(0.0)
len1 = if len1 >= 0 { len1 } else { def_len1 } len1 = if len1 >= 0 { len1 } else { def_len1 }
s := format_fl(f64(x), {pad_ch: pad_ch, len0: len0, len1: len1, positive: positive, sign_flag: sign, allign: allign}) s := format_fl(f64(x), {pad_ch: pad_ch, len0: len0, len1: len1, positive: positive, sign_flag: sign, allign: allign})
res.write(if ch == `F` {s.to_upper()} else {s}) res.write(if ch == `F` {s.to_upper()} else {s})
@ -789,7 +789,7 @@ pub fn v_sprintf(str string, pt ... voidptr) string{
else if ch in [`e`, `E`] { else if ch in [`e`, `E`] {
v_sprintf_panic(p_index, pt.len) v_sprintf_panic(p_index, pt.len)
x := unsafe {*(&f64(pt[p_index]))} x := unsafe {*(&f64(pt[p_index]))}
mut positive := x >= f64(0.0) positive := x >= f64(0.0)
len1 = if len1 >= 0 { len1 } else { def_len1 } len1 = if len1 >= 0 { len1 } else { def_len1 }
s := format_es(f64(x), {pad_ch: pad_ch, len0: len0, len1: len1, positive: positive, sign_flag: sign, allign: allign}) s := format_es(f64(x), {pad_ch: pad_ch, len0: len0, len1: len1, positive: positive, sign_flag: sign, allign: allign})
res.write(if ch == `E` {s.to_upper()} else {s}) res.write(if ch == `E` {s.to_upper()} else {s})
@ -801,7 +801,7 @@ pub fn v_sprintf(str string, pt ... voidptr) string{
else if ch in [`g`, `G`] { else if ch in [`g`, `G`] {
v_sprintf_panic(p_index, pt.len) v_sprintf_panic(p_index, pt.len)
x := unsafe {*(&f64(pt[p_index]))} x := unsafe {*(&f64(pt[p_index]))}
mut positive := x >= f64(0.0) positive := x >= f64(0.0)
mut s := "" mut s := ""
tx := fabs(x) tx := fabs(x)
if tx < 999_999.0 && tx >= 0.00001 { if tx < 999_999.0 && tx >= 0.00001 {

View File

@ -55,17 +55,17 @@ pub fn parse_rfc2822(s string) ?Time {
// also checks and support for leapseconds should be added in future PR // also checks and support for leapseconds should be added in future PR
pub fn parse_iso8601(s string) ?Time { pub fn parse_iso8601(s string) ?Time {
mut year := 0 year := 0
mut month := 0 month := 0
mut day := 0 day := 0
mut hour := 0 hour := 0
mut minute := 0 minute := 0
mut second := 0 second := 0
mut mic_second := 0 mic_second := 0
mut time_char := `a` time_char := `a`
mut plus_min := `a` plus_min := `a`
mut offset_hour := 0 offset_hour := 0
mut offset_min := 0 offset_min := 0
count := unsafe {C.sscanf(charptr(s.str), "%4d-%2d-%2d%c%2d:%2d:%2d.%6d%c%2d:%2d", &year, &month, &day, count := unsafe {C.sscanf(charptr(s.str), "%4d-%2d-%2d%c%2d:%2d:%2d.%6d%c%2d:%2d", &year, &month, &day,
&time_char, &hour, &minute, &time_char, &hour, &minute,

View File

@ -12,9 +12,9 @@ pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | CastExpr | pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | CastExpr |
ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral |
Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr | Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr |
MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectExpr | MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr |
SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | StructInit | Type | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | StructInit |
TypeOf | UnsafeExpr Type | TypeOf | UnsafeExpr
pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl | DeferStmt | pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl | DeferStmt |
EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt |
@ -491,8 +491,8 @@ pub mut:
pub struct UnsafeExpr { pub struct UnsafeExpr {
pub: pub:
expr Expr expr Expr
pos token.Position pos token.Position
} }
pub struct LockExpr { pub struct LockExpr {
@ -966,7 +966,8 @@ pub fn (expr Expr) is_blank_ident() bool {
pub fn (expr Expr) position() token.Position { pub fn (expr Expr) position() token.Position {
// all uncommented have to be implemented // all uncommented have to be implemented
match mut expr { match expr {
// KEKW2
AnonFn { AnonFn {
return expr.decl.pos return expr.decl.pos
} }
@ -1084,8 +1085,8 @@ pub fn (expr Expr) is_lvalue() bool {
pub fn (expr Expr) is_expr() bool { pub fn (expr Expr) is_expr() bool {
match expr { match expr {
IfExpr {return expr.is_expr} IfExpr { return expr.is_expr }
MatchExpr {return expr.is_expr} MatchExpr { return expr.is_expr }
else {} else {}
} }
return true return true
@ -1151,16 +1152,12 @@ pub fn (stmt Stmt) position() token.Position {
// field table.Field.default_expr, which should be ast.Expr // field table.Field.default_expr, which should be ast.Expr
pub fn fe2ex(x table.FExpr) Expr { pub fn fe2ex(x table.FExpr) Expr {
res := Expr{} res := Expr{}
unsafe { unsafe {C.memcpy(&res, &x, sizeof(Expr))}
C.memcpy(&res, &x, sizeof(Expr))
}
return res return res
} }
pub fn ex2fe(x Expr) table.FExpr { pub fn ex2fe(x Expr) table.FExpr {
res := table.FExpr{} res := table.FExpr{}
unsafe { unsafe {C.memcpy(&res, &x, sizeof(table.FExpr))}
C.memcpy(&res, &x, sizeof(table.FExpr))
}
return res return res
} }

View File

@ -37,8 +37,7 @@ pub fn (s &Scope) find_with_scope(name string) ?(ScopeObject, &Scope) {
} }
pub fn (s &Scope) find(name string) ?ScopeObject { pub fn (s &Scope) find(name string) ?ScopeObject {
for sc := s; true; sc = sc.parent for sc := s; true; sc = sc.parent {
{
if name in sc.objects { if name in sc.objects {
return sc.objects[name] return sc.objects[name]
} }
@ -85,6 +84,7 @@ pub fn (s &Scope) known_var(name string) bool {
} }
pub fn (mut s Scope) update_var_type(name string, typ table.Type) { pub fn (mut s Scope) update_var_type(name string, typ table.Type) {
s.end_pos = s.end_pos // TODO mut bug
match mut s.objects[name] { match mut s.objects[name] {
Var { Var {
if it.typ == typ { if it.typ == typ {

View File

@ -13,17 +13,12 @@ pub fn (node &FnDecl) modname() string {
} }
mut pamod := node.name.all_before_last('.') mut pamod := node.name.all_before_last('.')
if pamod == node.name.after('.') { if pamod == node.name.after('.') {
pamod = if node.is_builtin { pamod = if node.is_builtin { 'builtin' } else { 'main' }
'builtin'
} else {
'main'
}
} }
return pamod return pamod
} }
// These methods are used only by vfmt, vdoc, and for debugging. // These methods are used only by vfmt, vdoc, and for debugging.
pub fn (node &FnDecl) stringify(t &table.Table, cur_mod string) string { pub fn (node &FnDecl) stringify(t &table.Table, cur_mod string) string {
mut f := strings.new_builder(30) mut f := strings.new_builder(30)
if node.is_pub { if node.is_pub {
@ -32,7 +27,7 @@ pub fn (node &FnDecl) stringify(t &table.Table, cur_mod string) string {
mut receiver := '' mut receiver := ''
if node.is_method { if node.is_method {
mut styp := util.no_cur_mod(t.type_to_str(node.receiver.typ), cur_mod) mut styp := util.no_cur_mod(t.type_to_str(node.receiver.typ), cur_mod)
mut m := if node.rec_mut { node.receiver.typ.share().str() + ' ' } else { '' } m := if node.rec_mut { node.receiver.typ.share().str() + ' ' } else { '' }
if node.rec_mut { if node.rec_mut {
styp = styp[1..] // remove & styp = styp[1..] // remove &
} }
@ -51,11 +46,10 @@ pub fn (node &FnDecl) stringify(t &table.Table, cur_mod string) string {
mut name := if node.is_anon { '' } else { node.name.after('.') } mut name := if node.is_anon { '' } else { node.name.after('.') }
if node.language == .c { if node.language == .c {
name = 'C.$name' name = 'C.$name'
} } else if node.language == .js {
else if node.language == .js {
name = 'JS.$name' name = 'JS.$name'
} }
f.write('fn ${receiver}${name}') f.write('fn $receiver$name')
if node.is_generic { if node.is_generic {
f.write('<T>') f.write('<T>')
} }
@ -70,8 +64,8 @@ pub fn (node &FnDecl) stringify(t &table.Table, cur_mod string) string {
continue continue
} }
is_last_arg := i == node.args.len - 1 is_last_arg := i == node.args.len - 1
should_add_type := is_last_arg || node.args[i + 1].typ != arg.typ || (node.is_variadic && should_add_type := is_last_arg || node.args[i + 1].typ != arg.typ ||
i == node.args.len - 2) (node.is_variadic && i == node.args.len - 2)
if arg.is_mut { if arg.is_mut {
f.write(arg.typ.share().str() + ' ') f.write(arg.typ.share().str() + ' ')
} }
@ -105,7 +99,7 @@ pub fn (node &FnDecl) stringify(t &table.Table, cur_mod string) string {
} }
pub fn (x &InfixExpr) str() string { pub fn (x &InfixExpr) str() string {
return '${x.left.str()} $x.op.str() ${x.right.str()}' return '$x.left.str() $x.op.str() $x.right.str()'
} }
// Expressions in string interpolations may have to be put in braces if they // Expressions in string interpolations may have to be put in braces if they
@ -116,14 +110,14 @@ pub fn (x &InfixExpr) str() string {
// This method creates the format specifier (including the colon) or an empty // This method creates the format specifier (including the colon) or an empty
// string if none is needed and also returns (as bool) if the expression // string if none is needed and also returns (as bool) if the expression
// must be enclosed in braces. // must be enclosed in braces.
pub fn (lit &StringInterLiteral) get_fspec_braces(i int) (string, bool) { pub fn (lit &StringInterLiteral) get_fspec_braces(i int) (string, bool) {
mut res := []string{} mut res := []string{}
needs_fspec := lit.need_fmts[i] || lit.pluss[i] || (lit.fills[i] && lit.fwidths[i] >= 0) || lit.fwidths[i] != 0 || lit.precisions[i] != 0 needs_fspec := lit.need_fmts[i] || lit.pluss[i] ||
(lit.fills[i] && lit.fwidths[i] >= 0) || lit.fwidths[i] != 0 || lit.precisions[i] != 0
mut needs_braces := needs_fspec mut needs_braces := needs_fspec
if !needs_braces { if !needs_braces {
if i+1 < lit.vals.len && lit.vals[i+1].len > 0 { if i + 1 < lit.vals.len && lit.vals[i + 1].len > 0 {
next_char := lit.vals[i+1][0] next_char := lit.vals[i + 1][0]
if util.is_func_char(next_char) || next_char == `.` || next_char == `(` { if util.is_func_char(next_char) || next_char == `.` || next_char == `(` {
needs_braces = true needs_braces = true
} }
@ -181,90 +175,94 @@ pub fn (lit &StringInterLiteral) get_fspec_braces(i int) (string, bool) {
pub fn (x Expr) str() string { pub fn (x Expr) str() string {
match x { match x {
BoolLiteral { BoolLiteral {
return it.val.str() return x.val.str()
} }
CastExpr { CastExpr {
return '${it.typname}(${it.expr.str()})' return '${x.typname}($x.expr.str())'
} }
CallExpr { CallExpr {
sargs := args2str(it.args) sargs := args2str(x.args)
if it.is_method { if x.is_method {
return '${it.left.str()}.${it.name}($sargs)' return '${x.left.str()}.${x.name}($sargs)'
} }
return '${it.mod}.${it.name}($sargs)' return '${x.mod}.${x.name}($sargs)'
} }
CharLiteral { CharLiteral {
return '`$it.val`' return '`$x.val`'
} }
EnumVal { EnumVal {
return '.${it.val}' return '.$x.val'
} }
FloatLiteral { FloatLiteral {
return it.val return x.val
} }
Ident { Ident {
return it.name return x.name
} }
IndexExpr { IndexExpr {
return '${it.left.str()}[${it.index.str()}]' return '$x.left.str()[$x.index.str()]'
} }
IntegerLiteral { IntegerLiteral {
return it.val return x.val
} }
InfixExpr { InfixExpr {
return '${it.left.str()} $it.op.str() ${it.right.str()}' return '$x.left.str() $x.op.str() $x.right.str()'
} }
ParExpr { ParExpr {
return '($it.expr)' return '($x.expr)'
} }
PrefixExpr { PrefixExpr {
return it.op.str() + it.right.str() return x.op.str() + x.right.str()
} }
RangeExpr { RangeExpr {
mut s := '..' mut s := '..'
if it.has_low {s = '$it.low ' + s} if x.has_low {
if it.has_high {s = s + ' $it.high'} s = '$x.low ' + s
}
if x.has_high {
s = s + ' $x.high'
}
return s return s
} }
SelectorExpr { SelectorExpr {
return '${it.expr.str()}.${it.field_name}' return '${x.expr.str()}.$x.field_name'
} }
SizeOf { SizeOf {
return 'sizeof($it.expr)' return 'sizeof($x.expr)'
} }
StringInterLiteral { StringInterLiteral {
mut res := []string{} mut res := []string{}
res << "'" res << "'"
for i, val in it.vals { for i, val in x.vals {
res << val res << val
if i >= it.exprs.len { if i >= x.exprs.len {
break break
} }
res << '$' res << '$'
fspec_str, needs_braces := it.get_fspec_braces(i) fspec_str, needs_braces := x.get_fspec_braces(i)
if needs_braces { if needs_braces {
res << '{' res << '{'
res << it.exprs[i].str() res << x.exprs[i].str()
res << fspec_str res << fspec_str
res << '}' res << '}'
} else { } else {
res << it.exprs[i].str() res << x.exprs[i].str()
} }
} }
res << "'" res << "'"
return res.join('') return res.join('')
} }
StringLiteral { StringLiteral {
return '"$it.val"' return '"$x.val"'
} }
TypeOf { TypeOf {
return 'typeof(${it.expr.str()})' return 'typeof($x.expr.str())'
} }
Likely { Likely {
return '_likely_(${it.expr.str()})' return '_likely_($x.expr.str())'
} }
UnsafeExpr { UnsafeExpr {
return 'unsafe { $it.expr }' return 'unsafe { $x.expr }'
} }
else {} else {}
} }
@ -273,9 +271,9 @@ pub fn (x Expr) str() string {
pub fn (a CallArg) str() string { pub fn (a CallArg) str() string {
if a.is_mut { if a.is_mut {
return 'mut ${a.expr.str()}' return 'mut $a.expr.str()'
} }
return '${a.expr.str()}' return '$a.expr.str()'
} }
pub fn args2str(args []CallArg) string { pub fn args2str(args []CallArg) string {
@ -290,7 +288,7 @@ pub fn (node Stmt) str() string {
match node { match node {
AssignStmt { AssignStmt {
mut out := '' mut out := ''
for i, left in it.left { for i, left in node.left {
if left is Ident { if left is Ident {
var_info := left.var_info() var_info := left.var_info()
if var_info.is_mut { if var_info.is_mut {
@ -302,20 +300,20 @@ pub fn (node Stmt) str() string {
out += ',' out += ','
} }
} }
out += ' $it.op.str() ' out += ' $node.op.str() '
for i, val in it.right { for i, val in node.right {
out += val.str() out += val.str()
if i < it.right.len - 1 { if i < node.right.len - 1 {
out += ',' out += ','
} }
} }
return out return out
} }
ExprStmt { ExprStmt {
return it.expr.str() return node.expr.str()
} }
FnDecl { FnDecl {
return 'fn ${it.name}() { $it.stmts.len stmts }' return 'fn ${node.name}() { $node.stmts.len stmts }'
} }
else { else {
return '[unhandled stmt str type: ${typeof(node)} ]' return '[unhandled stmt str type: ${typeof(node)} ]'

View File

@ -96,10 +96,11 @@ pub fn (mut c Checker) check_scope_vars(sc &ast.Scope) {
} }
} }
} }
// TODO: fix all of these warnings if obj.is_mut && !obj.is_changed && !c.is_builtin_mod && obj.name != 'it' {
// if obj.is_mut && !obj.is_changed { // if obj.is_mut && !obj.is_changed && !c.is_builtin { //TODO C error bad field not checked
// c.warn('`$obj.name` is declared as mutable, but it was never changed', obj.pos) // c.warn('`$obj.name` is declared as mutable, but it was never changed',
// } // obj.pos)
}
} }
else {} else {}
} }
@ -555,7 +556,7 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
c.expected_type = former_expected_type c.expected_type = former_expected_type
} }
c.expected_type = table.void_type c.expected_type = table.void_type
mut left_type := c.expr(infix_expr.left) left_type := c.expr(infix_expr.left)
// left_type = c.unwrap_genric(c.expr(infix_expr.left)) // left_type = c.unwrap_genric(c.expr(infix_expr.left))
infix_expr.left_type = left_type infix_expr.left_type = left_type
c.expected_type = left_type c.expected_type = left_type
@ -816,6 +817,7 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
} }
// returns name and position of variable that needs write lock // returns name and position of variable that needs write lock
// also sets `is_changed` to true (TODO update the name to reflect this?)
fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Position) { fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Position) {
mut to_lock := '' // name of variable that needs lock mut to_lock := '' // name of variable that needs lock
mut pos := token.Position{} // and its position mut pos := token.Position{} // and its position
@ -1518,7 +1520,7 @@ pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type table.Type) t
return ret_type return ret_type
} }
pub fn (mut c Checker) check_or_expr(mut or_expr ast.OrExpr, ret_type table.Type) { pub fn (mut c Checker) check_or_expr(or_expr ast.OrExpr, ret_type table.Type) {
if or_expr.kind == .propagate { if or_expr.kind == .propagate {
if !c.cur_fn.return_type.has_flag(.optional) && c.cur_fn.name != 'main.main' { if !c.cur_fn.return_type.has_flag(.optional) && c.cur_fn.name != 'main.main' {
c.error('to propagate the optional call, `$c.cur_fn.name` must itself return an optional', c.error('to propagate the optional call, `$c.cur_fn.name` must itself return an optional',
@ -1536,7 +1538,7 @@ pub fn (mut c Checker) check_or_expr(mut or_expr ast.OrExpr, ret_type table.Type
// allow `f() or {}` // allow `f() or {}`
return return
} }
mut last_stmt := or_expr.stmts[stmts_len - 1] last_stmt := or_expr.stmts[stmts_len - 1]
if ret_type != table.void_type { if ret_type != table.void_type {
match mut last_stmt { match mut last_stmt {
ast.ExprStmt { ast.ExprStmt {
@ -2932,7 +2934,7 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) table.Type {
// since it is used in c.match_exprs() it saves checking twice // since it is used in c.match_exprs() it saves checking twice
node.cond_type = cond_type node.cond_type = cond_type
if cond_type == 0 { if cond_type == 0 {
c.error('match 0 cond type', node.pos) c.error('compiler bug: match 0 cond type', node.pos)
} }
cond_type_sym := c.table.get_type_symbol(cond_type) cond_type_sym := c.table.get_type_symbol(cond_type)
if cond_type_sym.kind !in [.sum_type, .interface_] { if cond_type_sym.kind !in [.sum_type, .interface_] {
@ -2981,7 +2983,11 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) table.Type {
// node.expected_type = c.expected_type // node.expected_type = c.expected_type
// } // }
node.return_type = ret_type node.return_type = ret_type
// println('!m $expr_type') if node.is_mut {
// Mark `x` in `match mut x {` as changed, and ensure it's mutable
// TODO2 enable when code is fixed
// c.fail_if_immutable(node.cond)
}
return ret_type return ret_type
} }

View File

@ -568,7 +568,7 @@ fn (p &Parser) fileis(s string) bool {
fn (mut p Parser) check_fn_mutable_arguments(typ table.Type, pos token.Position) { fn (mut p Parser) check_fn_mutable_arguments(typ table.Type, pos token.Position) {
sym := p.table.get_type_symbol(typ) sym := p.table.get_type_symbol(typ)
if sym.kind !in [.array, .struct_, .map, .placeholder] && !typ.is_ptr() { if sym.kind !in [.array, .struct_, .map, .placeholder, .sum_type] && !typ.is_ptr() {
p.error_with_pos('mutable arguments are only allowed for arrays, maps, and structs\n' + p.error_with_pos('mutable arguments are only allowed for arrays, maps, and structs\n' +
'return values instead: `fn foo(mut n $sym.name) {` => `fn foo(n $sym.name) $sym.name {`', 'return values instead: `fn foo(mut n $sym.name) {` => `fn foo(n $sym.name) $sym.name {`',
pos) pos)

View File

@ -229,6 +229,7 @@ fn (mut p Parser) match_expr() ast.MatchExpr {
typ: typ.to_ptr() typ: typ.to_ptr()
pos: cond_pos pos: cond_pos
is_used: true is_used: true
is_changed: true // TODO mut unchanged warning hack, remove
is_mut: is_mut is_mut: is_mut
}) })
} }

View File

@ -44,7 +44,7 @@ pub mut:
is_fmt bool // Used only for skipping ${} in strings, since we need literal is_fmt bool // Used only for skipping ${} in strings, since we need literal
// string values when generating formatted code. // string values when generating formatted code.
comments_mode CommentsMode comments_mode CommentsMode
is_inside_toplvl_statement bool // *only* used in comments_mode: .toplevel_comments, toggled by parser is_inside_toplvl_statement bool // *only* used in comments_mode: .toplevel_comments, toggled by parser
all_tokens []token.Token // *only* used in comments_mode: .toplevel_comments, contains all tokens all_tokens []token.Token // *only* used in comments_mode: .toplevel_comments, contains all tokens
tidx int tidx int
eofs int eofs int
@ -178,8 +178,8 @@ fn (mut s Scanner) ident_name() string {
return name return name
} }
// ident_fn_name look ahead and return name of function if possible, otherwise empty string // ident_fn_name looks ahead and returns name of the function if possible, otherwise an empty string
fn (mut s Scanner) ident_fn_name() string { fn (s &Scanner) ident_fn_name() string {
start := s.pos start := s.pos
mut pos := s.pos mut pos := s.pos
pos++ pos++
@ -221,9 +221,8 @@ fn (mut s Scanner) ident_fn_name() string {
if pos <= start || pos >= s.text.len { if pos <= start || pos >= s.text.len {
return '' return ''
} }
if s.text[start_pos].is_digit() || end_pos > s.text.len || if s.text[start_pos].is_digit() || end_pos > s.text.len || end_pos <= start_pos ||
end_pos <= start_pos || end_pos <= start || end_pos <= start || start_pos < start {
start_pos < start {
return '' return ''
} }
fn_name := s.text[start_pos..end_pos] fn_name := s.text[start_pos..end_pos]
@ -231,7 +230,7 @@ fn (mut s Scanner) ident_fn_name() string {
} }
// ident_mod_name look ahead and return name of module this file belongs to if possible, otherwise empty string // ident_mod_name look ahead and return name of module this file belongs to if possible, otherwise empty string
fn (mut s Scanner) ident_mod_name() string { fn (s &Scanner) ident_mod_name() string {
start := s.pos start := s.pos
mut pos := s.pos mut pos := s.pos
pos++ pos++
@ -259,7 +258,7 @@ fn (mut s Scanner) ident_mod_name() string {
} }
// ident_struct_name look ahead and return name of last encountered struct if possible, otherwise empty string // ident_struct_name look ahead and return name of last encountered struct if possible, otherwise empty string
fn (mut s Scanner) ident_struct_name() string { fn (s &Scanner) ident_struct_name() string {
start := s.pos start := s.pos
mut pos := s.pos mut pos := s.pos
// Return last known stuct_name encountered to avoid using high order/anonymous function definitions // Return last known stuct_name encountered to avoid using high order/anonymous function definitions
@ -303,9 +302,8 @@ fn (mut s Scanner) ident_struct_name() string {
return '' return ''
} }
start_pos := pos + 1 start_pos := pos + 1
if s.text[start_pos].is_digit() || end_pos > s.text.len || if s.text[start_pos].is_digit() || end_pos > s.text.len || end_pos <= start_pos ||
end_pos <= start_pos || end_pos <= start || end_pos <= start || start_pos <= start {
start_pos <= start {
return '' return ''
} }
struct_name := s.text[start_pos..end_pos] struct_name := s.text[start_pos..end_pos]
@ -354,8 +352,7 @@ fn (mut s Scanner) ident_bin_number() string {
} }
if s.text[s.pos - 1] == num_sep { if s.text[s.pos - 1] == num_sep {
s.error('cannot use `_` at the end of a numeric literal') s.error('cannot use `_` at the end of a numeric literal')
} } else if start_pos + 2 == s.pos {
else if start_pos + 2 == s.pos {
s.pos-- // adjust error position s.pos-- // adjust error position
s.error('number part of this binary is not provided') s.error('number part of this binary is not provided')
} else if has_wrong_digit { } else if has_wrong_digit {
@ -394,8 +391,7 @@ fn (mut s Scanner) ident_hex_number() string {
} }
if s.text[s.pos - 1] == num_sep { if s.text[s.pos - 1] == num_sep {
s.error('cannot use `_` at the end of a numeric literal') s.error('cannot use `_` at the end of a numeric literal')
} } else if start_pos + 2 == s.pos {
else if start_pos + 2 == s.pos {
s.pos-- // adjust error position s.pos-- // adjust error position
s.error('number part of this hexadecimal is not provided') s.error('number part of this hexadecimal is not provided')
} else if has_wrong_digit { } else if has_wrong_digit {
@ -434,8 +430,7 @@ fn (mut s Scanner) ident_oct_number() string {
} }
if s.text[s.pos - 1] == num_sep { if s.text[s.pos - 1] == num_sep {
s.error('cannot use `_` at the end of a numeric literal') s.error('cannot use `_` at the end of a numeric literal')
} } else if start_pos + 2 == s.pos {
else if start_pos + 2 == s.pos {
s.pos-- // adjust error position s.pos-- // adjust error position
s.error('number part of this octal is not provided') s.error('number part of this octal is not provided')
} else if has_wrong_digit { } else if has_wrong_digit {
@ -455,7 +450,7 @@ fn (mut s Scanner) ident_dec_number() string {
// scan integer part // scan integer part
for s.pos < s.text.len { for s.pos < s.text.len {
c := s.text[s.pos] c := s.text[s.pos]
if c == num_sep && s.text[s.pos + 1] == num_sep { if c == num_sep && s.text[s.pos + 1] == num_sep {
s.error('cannot use `_` consecutively') s.error('cannot use `_` consecutively')
} }
if !c.is_digit() && c != num_sep { if !c.is_digit() && c != num_sep {
@ -546,8 +541,7 @@ fn (mut s Scanner) ident_dec_number() string {
// error check: 5e // error check: 5e
s.pos-- // adjust error position s.pos-- // adjust error position
s.error('exponent has no digits') s.error('exponent has no digits')
} else if s.pos < s.text.len && } else if s.pos < s.text.len && s.text[s.pos] == `.` && !is_range && !call_method {
s.text[s.pos] == `.` && !is_range && !call_method {
// error check: 1.23.4, 123.e+3.4 // error check: 1.23.4, 123.e+3.4
if has_exp { if has_exp {
s.error('exponential part should be integer') s.error('exponential part should be integer')
@ -608,7 +602,7 @@ pub fn (mut s Scanner) scan_all_tokens_in_buffer() {
cmode := s.comments_mode cmode := s.comments_mode
s.comments_mode = .parse_comments s.comments_mode = .parse_comments
for { for {
mut t := s.text_scan() t := s.text_scan()
s.all_tokens << t s.all_tokens << t
if t.kind == .eof { if t.kind == .eof {
break break
@ -1093,7 +1087,7 @@ fn (mut s Scanner) text_scan() token.Token {
start := s.pos + 1 start := s.pos + 1
s.ignore_line() s.ignore_line()
mut comment_line_end := s.pos mut comment_line_end := s.pos
if s.text[s.pos-1] == `\r` { if s.text[s.pos - 1] == `\r` {
comment_line_end-- comment_line_end--
} else { } else {
// fix line_nr, \n was read; the comment is marked on the next line // fix line_nr, \n was read; the comment is marked on the next line
@ -1216,7 +1210,8 @@ fn (mut s Scanner) ident_string() string {
} }
// Don't allow \0 // Don't allow \0
if c == `0` && s.pos > 2 && s.text[s.pos - 1] == slash { if c == `0` && s.pos > 2 && s.text[s.pos - 1] == slash {
if (s.pos < s.text.len - 1 && s.text[s.pos + 1].is_digit()) || s.count_symbol_before(s.pos - 1, slash) % 2 == 0 { if (s.pos < s.text.len - 1 && s.text[s.pos + 1].is_digit()) ||
s.count_symbol_before(s.pos - 1, slash) % 2 == 0 {
} else if !is_cstr && !is_raw { } else if !is_cstr && !is_raw {
s.error(r'cannot use `\0` (NULL character) in the string literal') s.error(r'cannot use `\0` (NULL character) in the string literal')
} }
@ -1236,8 +1231,8 @@ fn (mut s Scanner) ident_string() string {
break break
} }
// $var // $var
if prevc == `$` && util.is_name_char(c) && !is_raw && if prevc == `$` && util.is_name_char(c) && !is_raw && s.count_symbol_before(s.pos - 2, slash) %
s.count_symbol_before(s.pos - 2, slash) % 2 == 0 { 2 == 0 {
s.is_inside_string = true s.is_inside_string = true
s.is_inter_start = true s.is_inter_start = true
s.pos -= 2 s.pos -= 2

View File

@ -13,7 +13,7 @@ const (
// compile_file compiles the content of a file by the given path as a template // compile_file compiles the content of a file by the given path as a template
pub fn compile_file(path, fn_name string) string { pub fn compile_file(path, fn_name string) string {
mut html := os.read_file(path) or { html := os.read_file(path) or {
panic('html failed') panic('html failed')
} }
return compile_template(html, fn_name) return compile_template(html, fn_name)
@ -82,7 +82,7 @@ _ = footer
pos := line.index('@include ') or { pos := line.index('@include ') or {
continue continue
} }
mut file_name := line[pos + 9..] file_name := line[pos + 9..]
file_path := os.join_path('templates', '${file_name}.html') file_path := os.join_path('templates', '${file_name}.html')
mut file_content := os.read_file(file_path) or { mut file_content := os.read_file(file_path) or {
panic('reading file $file_name failed') panic('reading file $file_name failed')