├── example.nim ├── .gitignore ├── .github └── FUNDING.yml ├── LICENSE ├── README.md ├── compiler ├── jstypes.nim └── jsgen.nim └── lib └── system ├── reprjs.nim └── jssys.nim /example.nim: -------------------------------------------------------------------------------- 1 | echo 42 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache/ 2 | nimblecache/ 3 | htmldocs/ 4 | Nim/ 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: juancarlospaco # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Juan Carlos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nim New Backend Template 2 | 3 | - Template to create a new Backend for Nim, based on the JavaScript Backend. 4 | - **Edit 4 files only to create your new Nim Backend**, as minimal as possible. 5 | - As example it implements a new Python Backend *(unfinished, PR Welcome)*. 6 | 7 | 8 | # Use 9 | 10 | ```console 11 | $ git clone https://github.com/juancarlospaco/nim-new-backend.git 12 | $ cd nim-new-backend 13 | $ git clone https://github.com/nim-lang/Nim.git 14 | 15 | $ cp --verbose --force compiler/jsgen.nim Nim/compiler/jsgen.nim 16 | 'compiler/jsgen.nim' -> 'Nim/compiler/jsgen.nim' 17 | 18 | $ cp --verbose --force compiler/jstypes.nim Nim/compiler/jstypes.nim 19 | 'compiler/jstypes.nim' -> 'Nim/compiler/jstypes.nim' 20 | 21 | $ cp --verbose --force lib/system/jssys.nim Nim/lib/system/jssys.nim 22 | 'lib/system/jssys.nim' -> 'Nim/lib/system/jssys.nim' 23 | 24 | $ cp --verbose --force lib/system/reprjs.nim Nim/lib/system/reprjs.nim 25 | 'lib/system/reprjs.nim' -> 'Nim/lib/system/reprjs.nim' 26 | 27 | $ nim c Nim/koch.nim 28 | $ Nim/koch temp js -d:release -d:danger -d:nodejs example.nim 29 | ``` 30 | 31 | - Open `example.js`. A new Nim compiler binary executable will be saved to `./Nim/bin/nim`. 32 | - Hack code until you get valid code generated, then compile the Nim compiler again. 33 | 34 | ```console 35 | $ Nim/koch temp js -d:release -d:danger -d:nodejs example.nim 36 | ``` 37 | 38 | **Code to look at:** 39 | See the dependency file generator, is a minimal working codegen: 40 | 41 | - https://github.com/nim-lang/Nim/blob/devel/compiler/depends.nim 42 | 43 | 44 | **Optional:** 45 | Rename `example.js` to the proper file extension you are compiling to, 46 | example `mv example.js example.py`. 47 | 48 | 49 | # Example 50 | 51 | ```nim 52 | echo 42 53 | ``` 54 | :arrow_up: Nim :arrow_up:          :arrow_down: Python :arrow_down: 55 | ```python 56 | #!/usr/bin/env python3 57 | # -*- coding: utf-8 -*- 58 | # Powered by Nim v1.1.1 https://nim-lang.org 59 | import sys 60 | from typing import * 61 | sys.dont_write_bytecode: Bool = True # type: Bool 62 | print(str("42")) 63 | ``` 64 | 65 | 66 | # Requisites 67 | 68 | - [Nim](https://nim-lang.org) 69 | 70 | 71 | # FAQ 72 | 73 | - Why not use Git SubRepos or Git SubTrees or Git SubModules?. 74 | 75 | If you know Git SubRepos and Git SubTrees and Git SubModules, 76 | then you wont need this simplified repo. 77 | 78 | But if you have an idea to make this *less* complex, not more complex, then send PR. 79 | 80 | - Why not use SymLinks?. 81 | 82 | It may produce errors on `import` and `include`. 83 | 84 | - But it generates a `*.js`?. 85 | 86 | Yes, but file extensions, tutorials, documentation, cosmetics, etc can be left for last details. 87 | 88 | - Why Python?. 89 | 90 | It is the second on popularity behind JavaScript, but [Nim](https://nim-lang.org) [already has JavaScript.](https://nim-lang.org/docs/backends.html#backends-the-javascript-target) 91 | 92 | To clean up code because JavaScript is kinda weird which also makes codegen weird. 93 | 94 | I imagine creating a Ruby or Go Backend from a Python backend is easier than from C. 95 | -------------------------------------------------------------------------------- /compiler/jstypes.nim: -------------------------------------------------------------------------------- 1 | ## Python Backend for Nim (CPython 3.8). 2 | 3 | template rope(arg: Int128): Rope = rope($arg) 4 | 5 | proc genTypeInfo(p: PProc, typ: PType): Rope 6 | proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope = 7 | var 8 | s, u: Rope 9 | field: PSym 10 | b: PNode 11 | result = nil 12 | case n.kind 13 | of nkRecList: 14 | if n.len == 1: 15 | result = genObjectFields(p, typ, n[0]) 16 | else: 17 | s = nil 18 | for i in 0.. 0: s.add(", \L") 20 | s.add(genObjectFields(p, typ, n[i])) 21 | result = ("{kind: 2, len: $1, offset: 0, " & 22 | "typ: null, name: null, sons: [$2]}") % [rope(n.len), s] 23 | of nkSym: 24 | field = n.sym 25 | s = genTypeInfo(p, field.typ) 26 | result = ("{kind: 1, offset: \"$1\", len: 0, " & 27 | "typ: $2, name: $3, sons: null}") % 28 | [mangleName(p.module, field), s, 29 | makeJSString(field.name.s)] 30 | of nkRecCase: 31 | if (n[0].kind != nkSym): internalError(p.config, n.info, "genObjectFields") 32 | field = n[0].sym 33 | s = genTypeInfo(p, field.typ) 34 | for i in 1.. 0: s.add(", \L") 78 | s.addf("{kind: 1, offset: \"Field$1\", len: 0, " & 79 | "typ: $2, name: \"Field$1\", sons: null}", 80 | [i.rope, genTypeInfo(p, typ[i])]) 81 | result = ("{kind: 2, len: $1, offset: 0, " & 82 | "typ: null, name: null, sons: [$2]}") % [rope(typ.len), s] 83 | 84 | proc genTupleInfo(p: PProc, typ: PType, name: Rope) = 85 | var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " & 86 | "finalizer: null};$n") % [name, rope(ord(typ.kind))] 87 | prepend(p.g.typeInfo, s) 88 | p.g.typeInfo.addf("var NNI$1 = $2;$n", 89 | [rope(typ.id), genTupleFields(p, typ)]) 90 | p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)]) 91 | 92 | proc genEnumInfo(p: PProc, typ: PType, name: Rope) = 93 | var s: Rope = nil 94 | for i in 0.. 0: s.add(", \L") 98 | let extName = if field.ast == nil: field.name.s else: field.ast.strVal 99 | s.addf("\"$1\": {kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}", 100 | [rope(field.position), name, makeJSString(extName)]) 101 | var n = ("var NNI$1 = {kind: 2, offset: 0, typ: null, " & 102 | "name: null, len: $2, sons: {$3}};$n") % [rope(typ.id), rope(typ.n.len), s] 103 | s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " & 104 | "finalizer: null};$n") % [name, rope(ord(typ.kind))] 105 | prepend(p.g.typeInfo, s) 106 | p.g.typeInfo.add(n) 107 | p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)]) 108 | if typ[0] != nil: 109 | p.g.typeInfo.addf("$1.base = $2;$n", 110 | [name, genTypeInfo(p, typ[0])]) 111 | 112 | proc genTypeInfo(p: PProc, typ: PType): Rope = 113 | let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned}) 114 | result = "NTI$1" % [rope(t.id)] 115 | if containsOrIncl(p.g.typeInfoGenerated, t.id): return 116 | case t.kind 117 | of tyDistinct: 118 | result = genTypeInfo(p, t[0]) 119 | of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64: 120 | var s = 121 | "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % 122 | [result, rope(ord(t.kind))] 123 | prepend(p.g.typeInfo, s) 124 | of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet: 125 | var s = 126 | "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % 127 | [result, rope(ord(t.kind))] 128 | prepend(p.g.typeInfo, s) 129 | p.g.typeInfo.addf("$1.base = $2;$n", 130 | [result, genTypeInfo(p, t.lastSon)]) 131 | of tyArray: 132 | var s = 133 | "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % 134 | [result, rope(ord(t.kind))] 135 | prepend(p.g.typeInfo, s) 136 | p.g.typeInfo.addf("$1.base = $2;$n", 137 | [result, genTypeInfo(p, t[1])]) 138 | of tyEnum: genEnumInfo(p, t, result) 139 | of tyObject: genObjectInfo(p, t, result) 140 | of tyTuple: genTupleInfo(p, t, result) 141 | of tyStatic: 142 | if t.n != nil: result = genTypeInfo(p, lastSon t) 143 | else: internalError(p.config, "genTypeInfo(" & $t.kind & ')') 144 | else: internalError(p.config, "genTypeInfo(" & $t.kind & ')') 145 | -------------------------------------------------------------------------------- /lib/system/reprjs.nim: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Nim's Runtime Library 4 | # (c) Copyright 2012 Andreas Rumpf 5 | # 6 | # See the file "copying.txt", included in this 7 | # distribution, for details about the copyright. 8 | # 9 | # The generic ``repr`` procedure for the javascript backend. 10 | 11 | proc reprInt(x: int64): string {.compilerproc.} = return $x 12 | proc reprFloat(x: float): string {.compilerproc.} = 13 | # Js toString doesn't differentiate between 1.0 and 1, 14 | # but we do. 15 | if $x == $(x.int): $x & ".0" 16 | else: $x 17 | 18 | proc reprPointer(p: pointer): string {.compilerproc.} = 19 | # Do we need to generate the full 8bytes ? In js a pointer is an int anyway 20 | var tmp: int 21 | {. emit: """ 22 | if (`p`_Idx == null) { 23 | `tmp` = 0; 24 | } else { 25 | `tmp` = `p`_Idx; 26 | } 27 | """ .} 28 | result = $tmp 29 | 30 | proc reprBool(x: bool): string {.compilerRtl.} = 31 | if x: result = "true" 32 | else: result = "false" 33 | 34 | proc isUndefined[T](x: T): bool {.inline.} = {.emit: "`result` = `x` === undefined;"} 35 | 36 | proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = 37 | if not typ.node.sons[e].isUndefined: 38 | result = makeNimstrLit(typ.node.sons[e].name) 39 | else: 40 | result = $e & " (invalid data!)" 41 | 42 | proc reprChar(x: char): string {.compilerRtl.} = 43 | result = "\'" 44 | case x 45 | of '"': add(result, "\\\"") 46 | of '\\': add(result, "\\\\") 47 | of '\127'..'\255', '\0'..'\31': add( result, "\\" & reprInt(ord(x)) ) 48 | else: add(result, x) 49 | add(result, "\'") 50 | 51 | proc reprStrAux(result: var string, s: cstring, len: int) = 52 | add(result, "\"") 53 | for i in 0 .. len-1: 54 | let c = s[i] 55 | case c 56 | of '"': add(result, "\\\"") 57 | of '\\': add(result, "\\\\") 58 | #of '\10': add(result, "\\10\"\n\"") 59 | of '\127'..'\255', '\0'..'\31': 60 | add(result, "\\" & reprInt(ord(c))) 61 | else: 62 | add(result, c) 63 | add(result, "\"") 64 | 65 | proc reprStr(s: string): string {.compilerRtl.} = 66 | result = "" 67 | var sIsNil = false 68 | asm """`sIsNil` = `s` === null""" 69 | if sIsNil: # cast[pointer](s).isNil: 70 | # Handle nil strings here because they don't have a length field in js 71 | # TODO: check for null/undefined before generating call to length in js? 72 | # Also: c backend repr of a nil string is "", but repr of an 73 | # array of string that is not initialized is [nil, nil, ...] ?? 74 | add(result, "nil") 75 | else: 76 | reprStrAux(result, s, s.len) 77 | 78 | proc addSetElem(result: var string, elem: int, typ: PNimType) = 79 | # Dispatch each set element to the correct repr proc 80 | case typ.kind: 81 | of tyEnum: add(result, reprEnum(elem, typ)) 82 | of tyBool: add(result, reprBool(bool(elem))) 83 | of tyChar: add(result, reprChar(chr(elem))) 84 | of tyRange: addSetElem(result, elem, typ.base) # Note the base to advance towards the element type 85 | of tyInt..tyInt64, tyUInt8, tyUInt16: add result, reprInt(elem) 86 | else: # data corrupt --> inform the user 87 | add(result, " (invalid data!)") 88 | 89 | iterator setKeys(s: int): int {.inline.} = 90 | # The type of s is a lie, but it's expected to be a set. 91 | # Iterate over the JS object representing a set 92 | # and returns the keys as int. 93 | var len: int 94 | var yieldRes: int 95 | var i: int = 0 96 | {. emit: """ 97 | var setObjKeys = Object.getOwnPropertyNames(`s`); 98 | `len` = setObjKeys.length; 99 | """ .} 100 | while i < len: 101 | {. emit: "`yieldRes` = parseInt(setObjKeys[`i`],10);\n" .} 102 | yield yieldRes 103 | inc i 104 | 105 | proc reprSetAux(result: var string, s: int, typ: PNimType) = 106 | add(result, "{") 107 | var first: bool = true 108 | for el in setKeys(s): 109 | if first: 110 | first = false 111 | else: 112 | add(result, ", ") 113 | addSetElem(result, el, typ.base) 114 | add(result, "}") 115 | 116 | proc reprSet(e: int, typ: PNimType): string {.compilerRtl.} = 117 | result = "" 118 | reprSetAux(result, e, typ) 119 | 120 | type 121 | ReprClosure {.final.} = object 122 | recDepth: int # do not recurse endlessly 123 | indent: int # indentation 124 | 125 | proc initReprClosure(cl: var ReprClosure) = 126 | cl.recDepth = -1 # default is to display everything! 127 | cl.indent = 0 128 | 129 | proc reprAux(result: var string, p: pointer, typ: PNimType, cl: var ReprClosure) 130 | 131 | proc reprArray(a: pointer, typ: PNimType, 132 | cl: var ReprClosure): string {.compilerRtl.} = 133 | var isNilArrayOrSeq: bool 134 | # isnil is not enough here as it would try to deref `a` without knowing what's inside 135 | {. emit: """ 136 | if (`a` == null) { 137 | `isNilArrayOrSeq` = true; 138 | } else if (`a`[0] == null) { 139 | `isNilArrayOrSeq` = true; 140 | } else { 141 | `isNilArrayOrSeq` = false; 142 | }; 143 | """ .} 144 | if typ.kind == tySequence and isNilArrayOrSeq: 145 | return "nil" 146 | 147 | # We prepend @ to seq, the C backend prepends the pointer to the seq. 148 | result = if typ.kind == tySequence: "@[" else: "[" 149 | var len: int = 0 150 | var i: int = 0 151 | 152 | {. emit: "`len` = `a`.length;\n" .} 153 | var dereffed: pointer = a 154 | for i in 0 .. len-1: 155 | if i > 0 : 156 | add(result, ", ") 157 | # advance pointer and point to element at index 158 | {. emit: """ 159 | `dereffed`_Idx = `i`; 160 | `dereffed` = `a`[`dereffed`_Idx]; 161 | """ .} 162 | reprAux(result, dereffed, typ.base, cl) 163 | 164 | add(result, "]") 165 | 166 | proc isPointedToNil(p: pointer): bool {.inline.}= 167 | {. emit: "if (`p` === null) {`result` = true};\n" .} 168 | 169 | proc reprRef(result: var string, p: pointer, typ: PNimType, 170 | cl: var ReprClosure) = 171 | if p.isPointedToNil: 172 | add(result , "nil") 173 | return 174 | add( result, "ref " & reprPointer(p) ) 175 | add(result, " --> ") 176 | if typ.base.kind != tyArray: 177 | {. emit: """ 178 | if (`p` != null && `p`.length > 0) { 179 | `p` = `p`[`p`_Idx]; 180 | } 181 | """ .} 182 | reprAux(result, p, typ.base, cl) 183 | 184 | proc reprRecordAux(result: var string, o: pointer, typ: PNimType, cl: var ReprClosure) = 185 | add(result, "[") 186 | 187 | var first: bool = true 188 | var val: pointer = o 189 | if typ.node.len == 0: 190 | # if the object has only one field, len is 0 and sons is nil, the field is in node 191 | let key: cstring = typ.node.name 192 | add(result, $key & " = ") 193 | {. emit: "`val` = `o`[`key`];\n" .} 194 | reprAux(result, val, typ.node.typ, cl) 195 | else: 196 | # if the object has more than one field, sons is not nil and contains the fields. 197 | for i in 0 .. typ.node.len-1: 198 | if first: first = false 199 | else: add(result, ",\n") 200 | 201 | let key: cstring = typ.node.sons[i].name 202 | add(result, $key & " = ") 203 | {. emit: "`val` = `o`[`key`];\n" .} # access the field by name 204 | reprAux(result, val, typ.node.sons[i].typ, cl) 205 | add(result, "]") 206 | 207 | proc reprRecord(o: pointer, typ: PNimType, cl: var ReprClosure): string {.compilerRtl.} = 208 | result = "" 209 | reprRecordAux(result, o, typ,cl) 210 | 211 | 212 | proc reprJSONStringify(p: int): string {.compilerRtl.} = 213 | # As a last resort, use stringify 214 | # We use this for tyOpenArray, tyVarargs while genTypeInfo is not implemented 215 | var tmp: cstring 216 | {. emit: "`tmp` = JSON.stringify(`p`);\n" .} 217 | result = $tmp 218 | 219 | proc reprAux(result: var string, p: pointer, typ: PNimType, 220 | cl: var ReprClosure) = 221 | if cl.recDepth == 0: 222 | add(result, "...") 223 | return 224 | dec(cl.recDepth) 225 | case typ.kind 226 | of tyInt..tyInt64, tyUInt..tyUInt64: 227 | add( result, reprInt(cast[int](p)) ) 228 | of tyChar: 229 | add( result, reprChar(cast[char](p)) ) 230 | of tyBool: 231 | add( result, reprBool(cast[bool](p)) ) 232 | of tyFloat..tyFloat128: 233 | add( result, reprFloat(cast[float](p)) ) 234 | of tyString: 235 | var fp: int 236 | {. emit: "`fp` = `p`;\n" .} 237 | add( result, reprStr(cast[string](p)) ) 238 | of tyCString: 239 | var fp: cstring 240 | {. emit: "`fp` = `p`;\n" .} 241 | if fp.isNil: 242 | add(result, "nil") 243 | else: 244 | reprStrAux(result, fp, fp.len) 245 | of tyEnum, tyOrdinal: 246 | var fp: int 247 | {. emit: "`fp` = `p`;\n" .} 248 | add(result, reprEnum(fp, typ)) 249 | of tySet: 250 | var fp: int 251 | {. emit: "`fp` = `p`;\n" .} 252 | add(result, reprSet(fp, typ)) 253 | of tyRange: reprAux(result, p, typ.base, cl) 254 | of tyObject, tyTuple: 255 | add(result, reprRecord(p, typ, cl)) 256 | of tyArray, tyArrayConstr, tySequence: 257 | add(result, reprArray(p, typ, cl)) 258 | of tyPointer: 259 | add(result, reprPointer(p)) 260 | of tyPtr, tyRef: 261 | reprRef(result, p, typ, cl) 262 | of tyProc: 263 | if p.isPointedToNil: 264 | add(result, "nil") 265 | else: 266 | add(result, reprPointer(p)) 267 | else: 268 | add( result, "(invalid data!)" & reprJsonStringify(cast[int](p)) ) 269 | inc(cl.recDepth) 270 | 271 | proc reprAny(p: pointer, typ: PNimType): string {.compilerRtl.} = 272 | var cl: ReprClosure 273 | initReprClosure(cl) 274 | result = "" 275 | reprAux(result, p, typ, cl) 276 | add(result, "\n") -------------------------------------------------------------------------------- /lib/system/jssys.nim: -------------------------------------------------------------------------------- 1 | ## Python Backend for Nim (CPython 3.8). 2 | import system/indexerrors # Used. do NOT remove. 3 | 4 | type 5 | PSafePoint = ptr SafePoint 6 | SafePoint {.compilerproc, final.} = object 7 | prev: PSafePoint # points to next safe point 8 | exc: ref Exception 9 | 10 | PCallFrame = ptr CallFrame 11 | CallFrame {.importc, nodecl, final.} = object 12 | prev: PCallFrame 13 | procname: cstring 14 | line: int # current line number 15 | filename: cstring 16 | 17 | PJSError = ref object 18 | columnNumber {.importc.}: int 19 | fileName {.importc.}: cstring 20 | lineNumber {.importc.}: int 21 | message {.importc.}: cstring 22 | stack {.importc.}: cstring 23 | 24 | JSRef = ref RootObj # Fake type. 25 | 26 | var 27 | framePtr {.importc, nodecl, volatile.}: PCallFrame 28 | excHandler {.importc, nodecl, volatile.}: int = 0 29 | lastJSError {.importc, nodecl, volatile.}: PJSError = nil 30 | 31 | {.push stacktrace: off, profiler:off.} 32 | proc nimBoolToStr(x: bool): string {.compilerproc.} = 33 | result = if x: "True" else: "False" 34 | 35 | proc nimCharToStr(x: char): string {.compilerproc.} = 36 | result = newString(1) 37 | result[0] = x 38 | 39 | proc isNimException(): bool {.asmNoStackFrame.} = 40 | asm "return `lastJSError` && `lastJSError`.m_type;" 41 | 42 | proc getCurrentException*(): ref Exception {.compilerRtl, benign.} = 43 | if isNimException(): result = cast[ref Exception](lastJSError) 44 | 45 | proc getCurrentExceptionMsg*(): string = 46 | asm "return str(sys.exec_info()[2])" 47 | 48 | proc auxWriteStackTrace(f: PCallFrame): string = 49 | type 50 | TempFrame = tuple[procname: cstring, line: int] 51 | var 52 | it = f 53 | i = 0 54 | total = 0 55 | tempFrames: array[0..63, TempFrame] 56 | while it != nil and i <= high(tempFrames): 57 | tempFrames[i].procname = it.procname 58 | tempFrames[i].line = it.line 59 | inc(i) 60 | inc(total) 61 | it = it.prev 62 | while it != nil: 63 | inc(total) 64 | it = it.prev 65 | result = "" 66 | # if the buffer overflowed print '...': 67 | if total != i: 68 | add(result, "(") 69 | add(result, $(total-i)) 70 | add(result, " calls omitted) ...\n") 71 | for j in countdown(i-1, 0): 72 | add(result, tempFrames[j].procname) 73 | if tempFrames[j].line > 0: 74 | add(result, ", line: ") 75 | add(result, $tempFrames[j].line) 76 | add(result, "\n") 77 | 78 | proc rawWriteStackTrace(): string = 79 | if framePtr != nil: 80 | result = "Traceback (most recent call last)\n" & auxWriteStackTrace(framePtr) 81 | else: 82 | result = "No stack traceback available\n" 83 | 84 | proc getStackTrace*(): string = rawWriteStackTrace() 85 | proc getStackTrace*(e: ref Exception): string = e.trace 86 | 87 | proc unhandledException(e: ref Exception) {. 88 | compilerproc, asmNoStackFrame.} = 89 | var buf = "" 90 | if e.msg.len != 0: 91 | add(buf, "Error: unhandled exception: ") 92 | add(buf, e.msg) 93 | else: 94 | add(buf, "Error: unhandled exception") 95 | add(buf, " [") 96 | add(buf, e.name) 97 | add(buf, "]\n") 98 | when NimStackTrace: 99 | add(buf, rawWriteStackTrace()) 100 | let cbuf = cstring(buf) 101 | framePtr = nil 102 | {.emit: """raise Exception(str(`cbuf`));""".} 103 | 104 | proc raiseException(e: ref Exception, ename: cstring) {.compilerproc, asmNoStackFrame.} = 105 | e.name = ename 106 | if excHandler == 0: unhandledException(e) 107 | when NimStackTrace: e.trace = rawWriteStackTrace() 108 | asm "raise `e`;" 109 | 110 | proc reraiseException() {.compilerproc, asmNoStackFrame.} = 111 | if lastJSError == nil: raise newException(ReraiseError, "no exception to reraise") 112 | else: 113 | if excHandler == 0: 114 | if isNimException(): unhandledException(cast[ref Exception](lastJSError)) 115 | 116 | asm "raise lastJSError;" 117 | 118 | proc raiseOverflow {.exportc: "raiseOverflow", noreturn, compilerProc.} = 119 | raise newException(OverflowError, "over- or underflow") 120 | 121 | proc raiseDivByZero {.exportc: "raiseDivByZero", noreturn, compilerProc.} = 122 | raise newException(DivByZeroError, "division by zero") 123 | 124 | proc raiseRangeError() {.compilerproc, noreturn.} = 125 | raise newException(RangeError, "value out of range") 126 | 127 | proc raiseIndexError(i, a, b: int) {.compilerproc, noreturn.} = 128 | raise newException(IndexError, formatErrorIndexBound(int(i), int(a), int(b))) 129 | 130 | proc raiseFieldError(f: string) {.compilerproc, noreturn.} = 131 | raise newException(FieldError, f) 132 | 133 | proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} = 134 | asm """ 135 | var result = {}; 136 | for (var i = 0; i < arguments.length; ++i) { 137 | var x = arguments[i]; 138 | if (typeof(x) == "object") { 139 | for (var j = x[0]; j <= x[1]; ++j) { 140 | result[j] = true; 141 | } 142 | } else { 143 | result[x] = true; 144 | } 145 | } 146 | return result; 147 | """ 148 | 149 | proc makeNimstrLit(c: cstring): string {.importcpp: "str(#)".} 150 | proc cstrToNimstr(c: cstring): string {.compilerproc, asmNoStackFrame.} = 151 | asm """return str(`c`)""" 152 | 153 | proc toJSStr(s: string): cstring {.importcpp: "str(#)".} 154 | proc mnewString(len: int): string {.importcpp: "str()".} 155 | 156 | proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} = 157 | # argument type is a fake 158 | asm """ 159 | result = 0 160 | for elem in range(`a`): 161 | result += 1 162 | return result 163 | """ 164 | 165 | proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} = 166 | asm """return bool(`a` == `b`)""" 167 | 168 | proc SetLe(a, b: int): bool {.compilerproc, asmNoStackFrame.} = 169 | asm """ 170 | for (var elem in `a`) { if (!`b`[elem]) return false; } 171 | return true; 172 | """ 173 | 174 | proc SetLt(a, b: int): bool {.compilerproc.} = 175 | result = SetLe(a, b) and not SetEq(a, b) 176 | 177 | proc SetMul(a, b: int): int {.compilerproc, asmNoStackFrame.} = 178 | asm """ 179 | var result = {}; 180 | for (var elem in `a`) { 181 | if (`b`[elem]) { result[elem] = true; } 182 | } 183 | return result; 184 | """ 185 | 186 | proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} = 187 | asm """ 188 | var result = {}; 189 | for (var elem in `a`) { result[elem] = true; } 190 | for (var elem in `b`) { result[elem] = true; } 191 | return result; 192 | """ 193 | 194 | proc SetMinus(a, b: int): int {.compilerproc, asmNoStackFrame.} = 195 | asm """ 196 | var result = {}; 197 | for (var elem in `a`) { 198 | if (!`b`[elem]) { result[elem] = true; } 199 | } 200 | return result; 201 | """ 202 | 203 | proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerProc.} = 204 | asm """ 205 | if (`a` == `b`) return 0; 206 | if (!`a`) return -1; 207 | if (!`b`) return 1; 208 | for (var i = 0; i < `a`.length && i < `b`.length; i++) { 209 | var result = `a`[i] - `b`[i]; 210 | if (result != 0) return result; 211 | } 212 | return `a`.length - `b`.length; 213 | """ 214 | 215 | proc cmp(x, y: string): int = 216 | return cmpStrings(x, y) 217 | 218 | proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} = 219 | asm """return bool(`a` == `b`)""" 220 | 221 | proc ewriteln(x: cstring) = 222 | asm """print(`x`)""" 223 | 224 | proc rawEcho {.compilerproc.} = 225 | asm """print(arguments)""" 226 | 227 | # Arithmetic: 228 | proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = 229 | asm """return int(`a` + `b`)""" 230 | 231 | proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = 232 | asm """return int(`a` - `b`)""" 233 | 234 | proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = 235 | asm """return int(`a` * `b`)""" 236 | 237 | proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = 238 | asm """return int(`a` / `b`)""" 239 | 240 | proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = 241 | asm """return int(`a` % `b`)""" 242 | 243 | proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = 244 | asm """return int(`a` + `b`)""" 245 | 246 | proc subInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = 247 | asm """return int(`a` - `b`)""" 248 | 249 | proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = 250 | asm """return int(`a` * `b`)""" 251 | 252 | proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = 253 | asm """return int(`a` / `b`)""" 254 | 255 | proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = 256 | asm """return int(`a` % `b`)""" 257 | 258 | proc negInt(a: int): int {.compilerproc.} = 259 | result = a*(-1) 260 | 261 | proc negInt64(a: int64): int64 {.compilerproc.} = 262 | result = a*(-1) 263 | 264 | proc absInt(a: int): int {.compilerproc.} = 265 | result = if a < 0: a*(-1) else: a 266 | 267 | proc absInt64(a: int64): int64 {.compilerproc.} = 268 | result = if a < 0: a*(-1) else: a 269 | 270 | when not defined(nimNoZeroExtendMagic): 271 | proc ze*(a: int): int {.compilerproc.} = 272 | result = a 273 | 274 | proc ze64*(a: int64): int64 {.compilerproc.} = 275 | result = a 276 | 277 | proc toU8*(a: int): int8 {.asmNoStackFrame, compilerproc.} = 278 | asm """return `a`""" 279 | 280 | proc toU16*(a: int): int16 {.asmNoStackFrame, compilerproc.} = 281 | asm """return `a`""" 282 | 283 | proc toU32*(a: int64): int32 {.asmNoStackFrame, compilerproc.} = 284 | asm """return `a`""" 285 | 286 | proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b 287 | proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b 288 | 289 | proc chckNilDisp(p: pointer) {.compilerproc.} = 290 | if p == nil: sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil") 291 | 292 | include "system/hti" 293 | 294 | proc isFatPointer(ti: PNimType): bool = 295 | # This has to be consistent with the code generator! 296 | return ti.base.kind notin {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, tySet, tyVar, tyRef, tyPtr} 297 | 298 | proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef {.compilerproc.} 299 | 300 | proc nimCopyAux(dest, src: JSRef, n: ptr TNimNode) {.compilerproc.} = 301 | case n.kind 302 | of nkNone: sysAssert(false, "nimCopyAux") 303 | of nkSlot: 304 | asm """ 305 | `dest`[`n`.offset] = nimCopy(`dest`[`n`.offset], `src`[`n`.offset], `n`.typ); 306 | """ 307 | of nkList: 308 | asm """ 309 | for (var i = 0; i < `n`.sons.length; i++) { 310 | nimCopyAux(`dest`, `src`, `n`.sons[i]); 311 | } 312 | """ 313 | of nkCase: 314 | asm """ 315 | `dest`[`n`.offset] = nimCopy(`dest`[`n`.offset], `src`[`n`.offset], `n`.typ); 316 | for (var i = 0; i < `n`.sons.length; ++i) { 317 | nimCopyAux(`dest`, `src`, `n`.sons[i][1]); 318 | } 319 | """ 320 | 321 | proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef = 322 | case ti.kind 323 | of tyPtr, tyRef, tyVar, tyNil: 324 | if not isFatPointer(ti): 325 | result = src 326 | else: 327 | asm "`result` = [`src`[0], `src`[1]];" 328 | of tySet: 329 | asm """ 330 | if (`dest` === null || `dest` === undefined) { 331 | `dest` = {}; 332 | } 333 | else { 334 | for (var key in `dest`) { delete `dest`[key]; } 335 | } 336 | for (var key in `src`) { `dest`[key] = `src`[key]; } 337 | `result` = `dest`; 338 | """ 339 | of tyTuple, tyObject: 340 | if ti.base != nil: result = nimCopy(dest, src, ti.base) 341 | elif ti.kind == tyObject: 342 | asm "`result` = (`dest` === null || `dest` === undefined) ? {m_type: `ti`} : `dest`;" 343 | else: 344 | asm "`result` = (`dest` === null || `dest` === undefined) ? {} : `dest`;" 345 | nimCopyAux(result, src, ti.node) 346 | of tySequence, tyArrayConstr, tyOpenArray, tyArray: 347 | asm """ 348 | if (`src` === null) { 349 | `result` = null; 350 | } 351 | else { 352 | if (`dest` === null || `dest` === undefined) { 353 | `dest` = new Array(`src`.length); 354 | } 355 | else { 356 | `dest`.length = `src`.length; 357 | } 358 | `result` = `dest`; 359 | for (var i = 0; i < `src`.length; ++i) { 360 | `result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base); 361 | } 362 | } 363 | """ 364 | of tyString: 365 | asm """ 366 | if (`src` !== null) { 367 | `result` = `src`.slice(0); 368 | } 369 | """ 370 | else: 371 | result = src 372 | 373 | proc genericReset(x: JSRef, ti: PNimType): JSRef {.compilerproc.} = 374 | asm "`result` = None" 375 | case ti.kind 376 | of tyPtr, tyRef, tyVar: 377 | if isFatPointer(ti): 378 | asm """`result` = [null, 0]""" 379 | of tyNil: 380 | asm """`result` = None""" 381 | of tySet: 382 | asm """`result` = {}""" 383 | of tyTuple: 384 | asm """`result` = tuple()""" 385 | of tyObject: 386 | asm "`result` = None" 387 | of tySequence, tyOpenArray: 388 | asm """`result` = []""" 389 | of tyArrayConstr, tyArray: 390 | asm """`result` = []""" 391 | else: discard 392 | 393 | proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {. 394 | asmNoStackFrame, compilerproc.} = 395 | # types are fake 396 | asm """ 397 | var result = new Array(`len`); 398 | for (var i = 0; i < `len`; ++i) result[i] = nimCopy(null, `value`, `typ`); 399 | return result; 400 | """ 401 | 402 | proc chckIndx(i, a, b: int): int {.compilerproc.} = 403 | if i >= a and i <= b: return i else: raiseIndexError(i, a, b) 404 | 405 | proc chckRange(i, a, b: int): int {.compilerproc.} = 406 | if i >= a and i <= b: return i else: raiseRangeError() 407 | 408 | proc chckObj(obj, subclass: PNimType) {.compilerproc.} = 409 | # checks if obj is of type subclass: 410 | var x = obj 411 | if x == subclass: return # optimized fast path 412 | while x != subclass: 413 | if x == nil: 414 | raise newException(ObjectConversionError, "invalid object conversion") 415 | x = x.base 416 | 417 | proc isObj(obj, subclass: PNimType): bool {.compilerproc.} = 418 | # checks if obj is of type subclass: 419 | var x = obj 420 | if x == subclass: return true # optimized fast path 421 | while x != subclass: 422 | if x == nil: return false 423 | x = x.base 424 | return true 425 | 426 | proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} = 427 | asm "str(`x`).__add__(`c`)" # Find better way? 428 | 429 | {.pop.} 430 | 431 | proc tenToThePowerOf(b: int): BiggestFloat = 432 | var b = b 433 | var a = 10.0 434 | result = 1.0 435 | while true: 436 | if (b and 1) == 1: result = result * a 437 | b = b shr 1 438 | if b == 0: break 439 | a = a * a 440 | 441 | const IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'} 442 | 443 | # XXX use JS's native way here 444 | proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start = 0): int {.compilerProc.} = 445 | var 446 | esign = 1.0 447 | sign = 1.0 448 | i = start 449 | exponent: int 450 | flags: int 451 | number = 0.0 452 | if s[i] == '+': inc(i) 453 | elif s[i] == '-': 454 | sign = -1.0 455 | inc(i) 456 | if s[i] == 'N' or s[i] == 'n': 457 | if s[i+1] == 'A' or s[i+1] == 'a': 458 | if s[i+2] == 'N' or s[i+2] == 'n': 459 | if s[i+3] notin IdentChars: 460 | number = NaN 461 | return i+3 - start 462 | return 0 463 | if s[i] == 'I' or s[i] == 'i': 464 | if s[i+1] == 'N' or s[i+1] == 'n': 465 | if s[i+2] == 'F' or s[i+2] == 'f': 466 | if s[i+3] notin IdentChars: 467 | number = Inf*sign 468 | return i+3 - start 469 | return 0 470 | while s[i] in {'0'..'9'}: 471 | # Read integer part 472 | flags = flags or 1 473 | number = number * 10.0 + toFloat(ord(s[i]) - ord('0')) 474 | inc(i) 475 | while s[i] == '_': inc(i) 476 | # Decimal? 477 | if s[i] == '.': 478 | var hd = 1.0 479 | inc(i) 480 | while s[i] in {'0'..'9'}: 481 | # Read fractional part 482 | flags = flags or 2 483 | number = number * 10.0 + toFloat(ord(s[i]) - ord('0')) 484 | hd = hd * 10.0 485 | inc(i) 486 | while s[i] == '_': inc(i) 487 | number = number / hd # this complicated way preserves precision 488 | # Again, read integer and fractional part 489 | if flags == 0: return 0 490 | # Exponent? 491 | if s[i] in {'e', 'E'}: 492 | inc(i) 493 | if s[i] == '+': 494 | inc(i) 495 | elif s[i] == '-': 496 | esign = -1.0 497 | inc(i) 498 | if s[i] notin {'0'..'9'}: 499 | return 0 500 | while s[i] in {'0'..'9'}: 501 | exponent = exponent * 10 + ord(s[i]) - ord('0') 502 | inc(i) 503 | while s[i] == '_': inc(i) 504 | # Calculate Exponent 505 | let hd = tenToThePowerOf(exponent) 506 | if esign > 0.0: number = number * hd 507 | else: number = number / hd 508 | # evaluate sign 509 | number = number * sign 510 | result = i - start 511 | -------------------------------------------------------------------------------- /compiler/jsgen.nim: -------------------------------------------------------------------------------- 1 | ## Python Backend for Nim (CPython 3.8). 2 | 3 | import 4 | ast, strutils, trees, magicsys, options, nversion, msgs, idents, types, tables, ropes, math, passes, ccgutils, 5 | wordrecg, renderer, intsets, cgmeth, lowerings, modulegraphs, lineinfos, rodutils, transf, injectdestructors 6 | 7 | from modulegraphs import ModuleGraph, PPassContext 8 | 9 | type 10 | TJSGen = object of PPassContext 11 | module: PSym 12 | graph: ModuleGraph 13 | config: ConfigRef 14 | sigConflicts: CountTable[SigHash] 15 | 16 | BModule = ref TJSGen 17 | TJSTypeKind = enum # necessary JS "types" 18 | etyNone = "None", # no type 19 | etyNull = "None", # null type 20 | etyProc = "Callable", # proc type 21 | etyBool = "Bool", # bool type 22 | etySeq = "List", # Nim seq or string type 23 | etyInt = "Int", # JavaScript's int 24 | etyFloat = "Float", # JavaScript's float 25 | etyString = "String", # JavaScript's string 26 | etyObject = "Any", # JavaScript's reference to an object 27 | etyBaseIndex = "Any" # base + index needed 28 | TResKind = enum 29 | resNone, # not set 30 | resExpr, # is some complex expression 31 | resVal, # is a temporary/value/l-value 32 | resCallee # expression is callee 33 | TCompRes = object 34 | kind: TResKind 35 | typ: TJSTypeKind 36 | res: Rope # result part; index if this is an 37 | # (address, index)-tuple 38 | address: Rope # address of an (address, index)-tuple 39 | tmpLoc: Rope # tmp var which stores the (address, index) 40 | # pair to prevent multiple evals. 41 | # the tmp is initialized upon evaling the 42 | # address. 43 | # might be nil. 44 | # (see `maybeMakeTemp`) 45 | 46 | TBlock = object 47 | id: int # the ID of the label; positive means that it 48 | # has been used (i.e. the label should be emitted) 49 | isLoop: bool # whether it's a 'block' or 'while' 50 | 51 | PGlobals = ref object of RootObj 52 | typeInfo, constants, code: Rope 53 | forwarded: seq[PSym] 54 | generatedSyms: IntSet 55 | typeInfoGenerated: IntSet 56 | unique: int # for temp identifier generation 57 | 58 | PProc = ref TProc 59 | TProc = object 60 | procDef: PNode 61 | prc: PSym 62 | globals, locals, body: Rope 63 | options: TOptions 64 | module: BModule 65 | g: PGlobals 66 | generatedParamCopies: IntSet 67 | unique: int # for temp identifier generation 68 | blocks: seq[TBlock] 69 | extraIndent: int 70 | up: PProc # up the call chain; required for closure support 71 | declaredGlobals: IntSet 72 | 73 | template config*(p: PProc): ConfigRef = p.module.config 74 | 75 | proc indentLine(p: PProc, r: Rope): Rope = 76 | result = r 77 | var p = p 78 | while true: 79 | for i in 0.. 0 and s.name.s[i-1] in {'a'..'z'}: x.add '_' 209 | x.add(chr(c.ord - 'A'.ord + 'a'.ord)) 210 | of 'a'..'z', '_', '0'..'9': x.add c 211 | else: x.add("hex_" & toHex(ord(c), 2)) 212 | inc i 213 | result = rope(x) 214 | s.loc.r = result 215 | 216 | template escapeJSString(s: string): string = s 217 | template makeJSString(s: string, escapeNonAscii = true): Rope = strutils.escape(s).rope 218 | 219 | include jstypes 220 | 221 | proc gen(p: PProc, n: PNode, r: var TCompRes) 222 | proc genStmt(p: PProc, n: PNode) 223 | proc genProc(oldProc: PProc, prc: PSym): Rope 224 | proc genConstant(p: PProc, c: PSym) 225 | 226 | template useMagic(p: PProc, name: string) = 227 | if name.len == 0: return 228 | var s = magicsys.getCompilerProc(p.module.graph, name) 229 | if s != nil: 230 | internalAssert p.config, s.kind in {skProc, skFunc, skMethod, skConverter} 231 | if not p.g.generatedSyms.containsOrIncl(s.id): 232 | let code = genProc(p, s) 233 | p.g.constants.add(code) 234 | else: 235 | if p.prc != nil: globalError(p.config, p.prc.info, "System module needs: " & name) 236 | else: rawMessage(p.config, errGenerated, "System module needs: " & name) 237 | 238 | func isSimpleExpr(p: PProc; n: PNode): bool = 239 | # calls all the way down --> can stay expression based 240 | if n.kind in nkCallKinds+{nkBracketExpr, nkDotExpr, nkPar, nkTupleConstr} or (n.kind in {nkObjConstr, nkBracket, nkCurly}): 241 | for c in n: 242 | if not p.isSimpleExpr(c): return false 243 | result = true 244 | elif n.isAtom: result = true 245 | 246 | proc getTemp(p: PProc, defineInLocals = true): Rope {.inline.} = 247 | inc p.unique 248 | result = "_tmp_$1" % [rope(p.unique)] 249 | if defineInLocals: p.locals.add(p.indentLine("$1$n" % [result])) 250 | 251 | proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) = 252 | assert r.kind == resNone 253 | var x, y: TCompRes 254 | if p.isSimpleExpr(a) and p.isSimpleExpr(b): 255 | gen(p, a, x) 256 | gen(p, b, y) 257 | r.kind = resExpr 258 | r.res = "bool($1 and $2)" % [x.rdLoc, y.rdLoc] 259 | else: 260 | r.res = p.getTemp 261 | r.kind = resVal 262 | # while a and b: 263 | # --> 264 | # while true: 265 | # aa 266 | # if not a: tmp = false 267 | # else: 268 | # bb 269 | # tmp = b 270 | # tmp 271 | gen(p, a, x) 272 | lineF(p, "if not(bool($1)):$n $2 = false; else:$n", [x.rdLoc, r.rdLoc]) 273 | p.nested: 274 | gen(p, b, y) 275 | lineF(p, "$2 = $1$n", [y.rdLoc, r.rdLoc]) 276 | line(p, "$n") 277 | 278 | proc genOr(p: PProc, a, b: PNode, r: var TCompRes) = 279 | assert r.kind == resNone 280 | var x, y: TCompRes 281 | if p.isSimpleExpr(a) and p.isSimpleExpr(b): 282 | gen(p, a, x) 283 | gen(p, b, y) 284 | r.kind = resExpr 285 | r.res = "bool($1 or $2)" % [x.rdLoc, y.rdLoc] 286 | else: 287 | r.res = p.getTemp 288 | r.kind = resVal 289 | gen(p, a, x) 290 | lineF(p, "if bool($1):$n $2 = true; else:$n", [x.rdLoc, r.rdLoc]) 291 | p.nested: 292 | gen(p, b, y) 293 | lineF(p, "$2 = $1$n", [y.rdLoc, r.rdLoc]) 294 | line(p, "$n") 295 | 296 | type 297 | TMagicFrmt = array[0..1, string] 298 | TMagicOps = array[mAddI..mStrToStr, TMagicFrmt] 299 | 300 | const jsMagics: TMagicOps = [ 301 | ["addInt", ""], # AddI 302 | ["subInt", ""], # SubI 303 | ["mulInt", ""], # MulI 304 | ["divInt", ""], # DivI 305 | ["modInt", ""], # ModI 306 | ["addInt", ""], # Succ 307 | ["subInt", ""], # Pred 308 | ["", ""], # AddF64 309 | ["", ""], # SubF64 310 | ["", ""], # MulF64 311 | ["", ""], # DivF64 312 | ["", ""], # ShrI 313 | ["", ""], # ShlI 314 | ["", ""], # AshrI 315 | ["", ""], # BitandI 316 | ["", ""], # BitorI 317 | ["", ""], # BitxorI 318 | ["nimMin", "nimMin"], # MinI 319 | ["nimMax", "nimMax"], # MaxI 320 | ["", ""], # addU 321 | ["", ""], # subU 322 | ["", ""], # mulU 323 | ["", ""], # divU 324 | ["", ""], # modU 325 | ["", ""], # EqI 326 | ["", ""], # LeI 327 | ["", ""], # LtI 328 | ["", ""], # EqF64 329 | ["", ""], # LeF64 330 | ["", ""], # LtF64 331 | ["", ""], # leU 332 | ["", ""], # ltU 333 | ["", ""], # leU64 334 | ["", ""], # ltU64 335 | ["", ""], # EqEnum 336 | ["", ""], # LeEnum 337 | ["", ""], # LtEnum 338 | ["", ""], # EqCh 339 | ["", ""], # LeCh 340 | ["", ""], # LtCh 341 | ["", ""], # EqB 342 | ["", ""], # LeB 343 | ["", ""], # LtB 344 | ["", ""], # EqRef 345 | ["", ""], # EqUntracedRef 346 | ["", ""], # LePtr 347 | ["", ""], # LtPtr 348 | ["", ""], # Xor 349 | ["", ""], # EqCString 350 | ["", ""], # EqProc 351 | ["negInt", ""], # UnaryMinusI 352 | ["negInt64", ""], # UnaryMinusI64 353 | ["absInt", ""], # AbsI 354 | ["", ""], # Not 355 | ["", ""], # UnaryPlusI 356 | ["", ""], # BitnotI 357 | ["", ""], # UnaryPlusF64 358 | ["", ""], # UnaryMinusF64 359 | ["nimCharToStr", "nimCharToStr"], 360 | ["nimBoolToStr", "nimBoolToStr"], 361 | ["cstrToNimstr", "cstrToNimstr"], 362 | ["cstrToNimstr", "cstrToNimstr"], 363 | ["cstrToNimstr", "cstrToNimstr"], 364 | ["cstrToNimstr", "cstrToNimstr"], 365 | ["", ""]] 366 | 367 | func needsTemp(p: PProc; n: PNode): bool {.inline.} = 368 | # check if n contains a call to determine if a temp should be made to prevent multiple evals 369 | if n.kind in nkCallKinds + {nkTupleConstr, nkObjConstr, nkBracket, nkCurly}: return true 370 | for c in n: 371 | if needsTemp(p, c): return true 372 | 373 | proc maybeMakeTemp(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rope] = 374 | var 375 | a = x.rdLoc 376 | b = a 377 | if needsTemp(p, n): # if we have tmp just use it 378 | if x.tmpLoc != nil and (mapType(n.typ) == etyBaseIndex or n.kind in {nkHiddenDeref, nkDerefExpr}): 379 | b = "$1[0][$1[1]]" % [x.tmpLoc] 380 | (a: a, tmp: b) 381 | else: 382 | let tmp = p.getTemp 383 | b = tmp 384 | a = "($1 = $2, $1)" % [tmp, a] 385 | (a: a, tmp: b) 386 | else: (a: a, tmp: b) 387 | 388 | template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = 389 | # $1 and $2 in the `frmt` string bind to lhs and rhs of the expr, 390 | # if $3 or $4 are present they will be substituted with temps for 391 | # lhs and rhs respectively 392 | var x, y: TCompRes 393 | useMagic(p, magic) 394 | gen(p, n[1], x) 395 | gen(p, n[2], y) 396 | var 397 | a, tmp = x.rdLoc 398 | b, tmp2 = y.rdLoc 399 | when "$3" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x) 400 | when "$4" in frmt: (b, tmp2) = maybeMakeTemp(p, n[2], y) 401 | r.res = frmt % [a, b, tmp, tmp2] 402 | r.kind = resExpr 403 | 404 | template unsignedTrimmerJS(size: BiggestInt): Rope = rope"" 405 | template unsignedTrimmer(size: BiggestInt): Rope = size.unsignedTrimmerJS 406 | 407 | proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, reassign = false) = 408 | var x, y: TCompRes 409 | gen(p, n[1], x) 410 | gen(p, n[2], y) 411 | let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size) 412 | if reassign: 413 | let (a, tmp) = maybeMakeTemp(p, n[1], x) 414 | r.res = "$1 = (($5 $2 $3) $4)" % [a, rope op, y.rdLoc, trimmer, tmp] 415 | else: 416 | r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer] 417 | r.kind = resExpr 418 | 419 | template ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = 420 | var x, y, z: TCompRes 421 | useMagic(p, magic) 422 | gen(p, n[1], x) 423 | gen(p, n[2], y) 424 | gen(p, n[3], z) 425 | r.res = frmt % [x.rdLoc, y.rdLoc, z.rdLoc] 426 | r.kind = resExpr 427 | 428 | template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = 429 | # $1 binds to n[1], if $2 is present it will be substituted to a tmp of $1 430 | useMagic(p, magic) 431 | gen(p, n[1], r) 432 | var a, tmp = r.rdLoc 433 | if "$2" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], r) 434 | r.res = frmt % [a, tmp] 435 | r.kind = resExpr 436 | 437 | proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = 438 | var 439 | x, y: TCompRes 440 | xLoc,yLoc: Rope 441 | let i = ord(optOverflowCheck notin p.options) 442 | useMagic(p, jsMagics[op][i]) 443 | if n.len > 2: 444 | gen(p, n[1], x) 445 | gen(p, n[2], y) 446 | xLoc = x.rdLoc 447 | yLoc = y.rdLoc 448 | else: 449 | gen(p, n[1], r) 450 | xLoc = r.rdLoc 451 | 452 | template applyFormat(frmtA, frmtB: string) = 453 | if i == 0: 454 | r.res = frmtA % [xLoc, yLoc] 455 | else: 456 | r.res = frmtB % [xLoc, yLoc] 457 | 458 | case op: 459 | of mAddI: applyFormat("addInt($1, $2)", "int($1 + $2)") 460 | of mSubI: applyFormat("subInt($1, $2)", "int($1 - $2)") 461 | of mMulI: applyFormat("mulInt($1, $2)", "int($1 * $2)") 462 | of mDivI: applyFormat("divInt($1, $2)", "int($1 / $2)") 463 | of mModI: applyFormat("modInt($1, $2)", "int($1 % $2)") 464 | of mSucc: applyFormat("addInt($1, $2)", "int($1 + $2)") 465 | of mPred: applyFormat("subInt($1, $2)", "int($1 - $2)") 466 | of mAddF64: applyFormat("($1 + $2)", "float($1 + $2)") 467 | of mSubF64: applyFormat("($1 - $2)", "float($1 - $2)") 468 | of mMulF64: applyFormat("($1 * $2)", "float($1 * $2)") 469 | of mDivF64: applyFormat("($1 / $2)", "float($1 / $2)") 470 | of mShrI: applyFormat("", "") 471 | of mShlI: applyFormat("($1 << $2)", "int($1 << $2)") 472 | of mAshrI: applyFormat("($1 >> $2)", "int($1 >> $2)") 473 | of mBitandI: applyFormat("($1 & $2)", "int($1 & $2)") 474 | of mBitorI: applyFormat("($1 | $2)", "int($1 | $2)") 475 | of mBitxorI: applyFormat("($1 ^ $2)", "int($1 ^ $2)") 476 | of mMinI: applyFormat("nimMin($1, $2)", "min($1, $2)") 477 | of mMaxI: applyFormat("nimMax($1, $2)", "max($1, $2)") 478 | of mAddU: applyFormat("", "") 479 | of mSubU: applyFormat("", "") 480 | of mMulU: applyFormat("", "") 481 | of mDivU: applyFormat("", "") 482 | of mModU: applyFormat("($1 % $2)", "int($1 % $2)") 483 | of mEqI: applyFormat("($1 == $2)", "bool($1 == $2)") 484 | of mLeI: applyFormat("($1 <= $2)", "bool($1 <= $2)") 485 | of mLtI: applyFormat("($1 < $2)", "bool($1 < $2)") 486 | of mEqF64: applyFormat("($1 == $2)", "bool($1 == $2)") 487 | of mLeF64: applyFormat("($1 <= $2)", "bool($1 <= $2)") 488 | of mLtF64: applyFormat("($1 < $2)", "bool($1 < $2)") 489 | of mLeU: applyFormat("($1 <= $2)", "bool($1 <= $2)") 490 | of mLtU: applyFormat("($1 < $2)", "bool($1 < $2)") 491 | of mLeU64: applyFormat("($1 <= $2)", "bool($1 <= $2)") 492 | of mLtU64: applyFormat("($1 < $2)", "bool($1 < $2)") 493 | of mEqEnum: applyFormat("($1 == $2)", "bool($1 == $2)") 494 | of mLeEnum: applyFormat("($1 <= $2)", "bool($1 <= $2)") 495 | of mLtEnum: applyFormat("($1 < $2)", "bool($1 < $2)") 496 | of mEqCh: applyFormat("($1 == $2)", "bool($1 == $2)") 497 | of mLeCh: applyFormat("($1 <= $2)", "bool($1 <= $2)") 498 | of mLtCh: applyFormat("($1 < $2)", "bool($1 < $2)") 499 | of mEqB: applyFormat("($1 == $2)", "bool($1 == $2)") 500 | of mLeB: applyFormat("($1 <= $2)", "bool($1 <= $2)") 501 | of mLtB: applyFormat("($1 < $2)", "bool($1 < $2)") 502 | of mEqRef: applyFormat("($1 == $2)", "bool($1 == $2)") 503 | of mEqUntracedRef: applyFormat("($1 == $2)", "bool($1 == $2)") 504 | of mLePtr: applyFormat("($1 <= $2)", "bool($1 <= $2)") 505 | of mLtPtr: applyFormat("($1 < $2)", "bool($1 < $2)") 506 | of mXor: applyFormat("($1 != $2)", "bool($1 != $2)") 507 | of mEqCString: applyFormat("($1 == $2)", "bool($1 == $2)") 508 | of mEqProc: applyFormat("($1 == $2)", "bool($1 == $2)") 509 | of mUnaryMinusI: applyFormat("negInt($1)", "int(-$1)") 510 | of mUnaryMinusI64: applyFormat("negInt64($1)", "int(-$1)") 511 | of mAbsI: applyFormat("absInt($1)", "abs($1)") 512 | of mNot: applyFormat("!($1)", "not($1)") 513 | of mUnaryPlusI: applyFormat("+($1)", "int(abs($1))") 514 | of mBitnotI: applyFormat("~($1)", "int(~$1)") 515 | of mUnaryPlusF64: applyFormat("+($1)", "float(abs($1))") 516 | of mUnaryMinusF64: applyFormat("-($1)", "float(-$1)") 517 | of mCharToStr: applyFormat("nimCharToStr($1)", "str($1)") 518 | of mBoolToStr: applyFormat("nimBoolToStr($1)", "str($1)") 519 | of mIntToStr: applyFormat("cstrToNimstr(($1)+\"\")", "str($1)") 520 | of mInt64ToStr: applyFormat("cstrToNimstr(($1)+\"\")", "str($1)") 521 | of mFloatToStr: applyFormat("cstrToNimstr(($1)+\"\")", "str($1)") 522 | of mCStrToStr: applyFormat("cstrToNimstr($1)", "str($1)") 523 | of mStrToStr, mUnown: applyFormat("$1", "str($1)") 524 | else: doAssert false, $op 525 | 526 | proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = 527 | case op 528 | of mAddU: binaryUintExpr(p, n, r, "+") 529 | of mSubU: binaryUintExpr(p, n, r, "-") 530 | of mMulU: binaryUintExpr(p, n, r, "*") 531 | of mDivU: binaryUintExpr(p, n, r, "/") 532 | of mDivI: arithAux(p, n, r, op) 533 | of mModI: arithAux(p, n, r, op) 534 | of mShrI: 535 | var x, y: TCompRes 536 | gen(p, n[1], x) 537 | gen(p, n[2], y) 538 | let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size) 539 | r.res = "int($1$2 >> $3)" % [x.rdLoc, trimmer, y.rdLoc] 540 | of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, 541 | mCStrToStr, mStrToStr, mEnumToStr: 542 | arithAux(p, n, r, op) 543 | of mEqRef, mEqUntracedRef: 544 | if mapType(n[1].typ) != etyBaseIndex: 545 | arithAux(p, n, r, op) 546 | else: 547 | var x, y: TCompRes 548 | gen(p, n[1], x) 549 | gen(p, n[2], y) 550 | r.res = "bool($# == $# and $# == $#)" % [x.address, y.address, x.res, y.res] 551 | else: 552 | arithAux(p, n, r, op) 553 | r.kind = resExpr 554 | 555 | template hasFrameInfo(p: PProc): bool = 556 | ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and ((p.prc == nil) or not (sfPure in p.prc.flags)) 557 | 558 | template genLineDir(p: PProc, n: PNode) = 559 | let line = toLinenumber(n.info) 560 | if optLineDir in p.options: lineF(p, "# line $2 \"$1\"$n", [rope(toFilename(p.config, n.info)), rope(line)]) 561 | if hasFrameInfo(p): lineF(p, "# F.line = $1$n", [rope(line)]) 562 | 563 | proc genWhileStmt(p: PProc, n: PNode) = 564 | var cond: TCompRes 565 | internalAssert p.config, isEmptyType(n.typ) 566 | genLineDir(p, n) 567 | inc(p.unique) 568 | setLen(p.blocks, p.blocks.len + 1) 569 | p.blocks[^1].id = -p.unique 570 | p.blocks[^1].isLoop = true 571 | let labl = p.unique.rope 572 | lineF(p, "while True: # L$1 $n", [labl]) # We need a conditional here, but JS uses a Label instead? 573 | p.nested: gen(p, n[0], cond) 574 | # lineF(p, "if bool(not($1)): break # L$2$n", [cond.res, labl]) # Here JS breaks using Label. 575 | p.nested: genStmt(p, n[1]) 576 | lineF(p, "$n", [labl]) 577 | setLen(p.blocks, p.blocks.len - 1) 578 | 579 | proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) = 580 | if src.kind != resNone: 581 | if dest.kind != resNone: 582 | lineF(p, "$1 = $2$n", [dest.rdLoc, src.rdLoc]) 583 | else: 584 | lineF(p, "$1$n", [src.rdLoc]) 585 | src.kind = resNone 586 | src.res = nil 587 | 588 | proc genTry(p: PProc, n: PNode, r: var TCompRes) = 589 | # code to generate: 590 | # 591 | # ++excHandler; 592 | # var tmpFramePtr = framePtr; 593 | # try { 594 | # stmts; 595 | # --excHandler; 596 | # } catch (EXC) { 597 | # var prevJSError = lastJSError; lastJSError = EXC; 598 | # framePtr = tmpFramePtr; 599 | # --excHandler; 600 | # if (e.typ && e.typ == NTI433 || e.typ == NTI2321) { 601 | # stmts; 602 | # } else if (e.typ && e.typ == NTI32342) { 603 | # stmts; 604 | # } else { 605 | # stmts; 606 | # } 607 | # lastJSError = prevJSError; 608 | # } finally { 609 | # framePtr = tmpFramePtr; 610 | # stmts; 611 | # } 612 | genLineDir(p, n) 613 | if not isEmptyType(n.typ): 614 | r.kind = resVal 615 | r.res = getTemp(p) 616 | inc(p.unique) 617 | var i = 1 618 | var catchBranchesExist = n.len > 1 and n[i].kind == nkExceptBranch 619 | var tmpFramePtr = rope"F" 620 | lineF(p, "try:$n", []) 621 | var a: TCompRes 622 | p.nested: 623 | gen(p, n[0], a) 624 | moveInto(p, a, r) 625 | var generalCatchBranchExists = false 626 | while i < n.len and n[i].kind == nkExceptBranch: 627 | if n[i].len == 1: 628 | # general except section: 629 | generalCatchBranchExists = true 630 | if i > 1: lineF(p, "except Exception:$n", []) 631 | p.nested: 632 | gen(p, n[i][0], a) 633 | moveInto(p, a, r) 634 | if i > 1: lineF(p, "$n", []) 635 | else: 636 | var orExpr: Rope = nil 637 | var excAlias: PNode = nil 638 | 639 | useMagic(p, "isObj") 640 | for j in 0.. 1: line(p, "except: $n") 667 | lineF(p, "if (lastJSError && ($1)) {$n", [orExpr]) 668 | # If some branch requires a local alias introduce it here. This is needed 669 | # since JS cannot do ``catch x as y``. 670 | if excAlias != nil: 671 | excAlias.sym.loc.r = mangleName(p.module, excAlias.sym) 672 | lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.r) 673 | gen(p, n[i][^1], a) 674 | moveInto(p, a, r) 675 | lineF(p, "$n", []) 676 | inc(i) 677 | if catchBranchesExist: 678 | if not generalCatchBranchExists: 679 | useMagic(p, "reraiseException") 680 | line(p, "except Exception as e:\L") 681 | p.nested: 682 | line(p, "\traise e\L") 683 | line(p, "$n") 684 | line(p, "finally:$n") 685 | p.nested: 686 | if i < n.len and n[i].kind == nkFinally: 687 | genStmt(p, n[i][0]) 688 | line(p, "$n") 689 | 690 | proc genRaiseStmt(p: PProc, n: PNode) = 691 | if n[0].kind != nkEmpty: 692 | var a: TCompRes 693 | gen(p, n[0], a) 694 | let typ = skipTypes(n[0].typ, abstractPtrs) 695 | genLineDir(p, n) 696 | lineF(p, "raise Exception(str($1, $2))$n", [a.rdLoc, typ.sym.name.s.rope]) 697 | else: 698 | genLineDir(p, n) 699 | line(p, "raise $n\L") 700 | 701 | proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = 702 | var cond, stmt: TCompRes 703 | genLineDir(p, n) 704 | gen(p, n[0], cond) 705 | let stringSwitch = skipTypes(n[0].typ, abstractVar).kind == tyString 706 | if stringSwitch: 707 | useMagic(p, "toJSStr") 708 | lineF(p, "switch (toJSStr($1)) {$n", [cond.rdLoc]) 709 | else: 710 | lineF(p, "switch ($1) {$n", [cond.rdLoc]) 711 | if not isEmptyType(n.typ): 712 | r.kind = resVal 713 | r.res = getTemp(p) 714 | for i in 1..= 0 and not p.blocks[idx].isLoop: dec idx 778 | if idx < 0 or not p.blocks[idx].isLoop: 779 | internalError(p.config, n.info, "no loop to break") 780 | p.blocks[idx].id = abs(p.blocks[idx].id) # label is used 781 | lineF(p, "break # L$1$n", [rope(p.blocks[idx].id)]) 782 | 783 | proc genAsmOrEmitStmt(p: PProc, n: PNode) = 784 | genLineDir(p, n) 785 | p.body.add p.indentLine(nil) 786 | for i in 0.. 0: 828 | lineF(p, "else:$n", []) 829 | inc(toClose) 830 | p.nested: gen(p, it[0], cond) 831 | lineF(p, "if bool($1):$n", [cond.rdLoc]) 832 | p.nested: gen(p, it[1], stmt) 833 | else: # else part: 834 | lineF(p, "else:$n", []) 835 | p.nested: gen(p, it[0], stmt) 836 | moveInto(p, stmt, r) 837 | line(p, repeat("$n", toClose) & "\L") 838 | 839 | proc generateHeader(p: PProc, typ: PType): Rope = 840 | result = nil 841 | for i in 1..= pos: 902 | # dest[i].shallowCopy(dest[j]) 903 | # See bug #5933. So we try to be more compatible with the C backend semantics 904 | # here for 'shallowCopy'. This is an educated guess and might require further 905 | # changes later: 906 | let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyOpt, tyString} 907 | genAsgnAux(p, n[0], n[1], noCopyNeeded=noCopy) 908 | 909 | proc genSwap(p: PProc, n: PNode) = 910 | var a, b: TCompRes 911 | gen(p, n[1], a) 912 | gen(p, n[2], b) 913 | var tmp = p.getTemp(false) 914 | if mapType(p, skipTypes(n[1].typ, abstractVar)) == etyBaseIndex: 915 | let tmp2 = p.getTemp(false) 916 | if a.typ != etyBaseIndex or b.typ != etyBaseIndex: 917 | internalError(p.config, n.info, "genSwap") 918 | lineF(p, "$1 = $2; $2 = $3; $3 = $1;$n", [tmp, a.address, b.address]) 919 | tmp = tmp2 920 | lineF(p, "$1 = $2; $2 = $3; $3 = $1;", [tmp, a.res, b.res]) 921 | 922 | proc getFieldPosition(p: PProc; f: PNode): int {.inline.} = 923 | case f.kind 924 | of nkIntLit..nkUInt64Lit: result = int(f.intVal) 925 | of nkSym: result = f.sym.position 926 | else: internalError(p.config, f.info, "genFieldPosition") 927 | 928 | proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = 929 | var a: TCompRes 930 | r.typ = etyBaseIndex 931 | let b = if n.kind == nkHiddenAddr: n[0] else: n 932 | gen(p, b[0], a) 933 | if skipTypes(b[0].typ, abstractVarRange).kind == tyTuple: 934 | r.res = makeJSString("Field" & $getFieldPosition(p, b[1])) 935 | else: 936 | if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr") 937 | var f = b[1].sym 938 | if f.loc.r == nil: f.loc.r = mangleName(p.module, f) 939 | r.res = makeJSString($f.loc.r) 940 | internalAssert p.config, a.typ != etyBaseIndex 941 | r.address = a.res 942 | r.kind = resExpr 943 | 944 | proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = 945 | gen(p, n[0], r) 946 | r.typ = mapType(n.typ) 947 | let otyp = skipTypes(n[0].typ, abstractVarRange) 948 | 949 | template mkTemp(i: int) = 950 | if r.typ == etyBaseIndex: 951 | if needsTemp(p, n[i]): 952 | let tmp = p.getTemp 953 | r.address = "($1 = $2, $1)[0]" % [tmp, r.res] 954 | r.res = "$1[1]" % [tmp] 955 | r.tmpLoc = tmp 956 | else: 957 | r.address = "$1[0]" % [r.res] 958 | r.res = "$1[1]" % [r.res] 959 | if otyp.kind == tyTuple: 960 | r.res = ("$1.Field$2") % 961 | [r.res, getFieldPosition(p, n[1]).rope] 962 | mkTemp(0) 963 | else: 964 | if n[1].kind != nkSym: internalError(p.config, n[1].info, "genFieldAccess") 965 | var f = n[1].sym 966 | if f.loc.r == nil: f.loc.r = mangleName(p.module, f) 967 | r.res = "$1.$2" % [r.res, f.loc.r] 968 | mkTemp(1) 969 | r.kind = resExpr 970 | 971 | proc genAddr(p: PProc, n: PNode, r: var TCompRes) 972 | 973 | proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) = 974 | internalAssert p.config, n.kind == nkCheckedFieldExpr 975 | # nkDotExpr to access the requested field 976 | let accessExpr = n[0] 977 | # nkCall to check if the discriminant is valid 978 | var checkExpr = n[1] 979 | 980 | let negCheck = checkExpr[0].sym.magic == mNot 981 | if negCheck: 982 | checkExpr = checkExpr[^1] 983 | 984 | # Field symbol 985 | var field = accessExpr[1].sym 986 | internalAssert p.config, field.kind == skField 987 | if field.loc.r == nil: field.loc.r = mangleName(p.module, field) 988 | # Discriminant symbol 989 | let disc = checkExpr[2].sym 990 | internalAssert p.config, disc.kind == skField 991 | if disc.loc.r == nil: disc.loc.r = mangleName(p.module, disc) 992 | 993 | var setx: TCompRes 994 | gen(p, checkExpr[1], setx) 995 | 996 | var obj: TCompRes 997 | gen(p, accessExpr[0], obj) 998 | # Avoid evaluating the LHS twice (one to read the discriminant and one to read 999 | # the field) 1000 | let tmp = p.getTemp() 1001 | lineF(p, "$1 = $2$n", tmp, obj.res) 1002 | 1003 | useMagic(p, "raiseFieldError") 1004 | #useMagic(p, "makeNimstrLit") 1005 | let msg = genFieldError(field, disc) 1006 | lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError(str($5)); }$n", 1007 | setx.res, tmp, disc.loc.r, if negCheck: ~"!==" else: ~"===", 1008 | makeJSString(msg)) 1009 | 1010 | if addrTyp != nil and mapType(p, addrTyp) == etyBaseIndex: 1011 | r.typ = etyBaseIndex 1012 | r.res = makeJSString($field.loc.r) 1013 | r.address = tmp 1014 | else: 1015 | r.typ = etyNone 1016 | r.res = "$1.$2" % [tmp, field.loc.r] 1017 | r.kind = resExpr 1018 | 1019 | proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = 1020 | var 1021 | a, b: TCompRes 1022 | first: Int128 1023 | r.typ = etyBaseIndex 1024 | let m = if n.kind == nkHiddenAddr: n[0] else: n 1025 | gen(p, m[0], a) 1026 | gen(p, m[1], b) 1027 | #internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex 1028 | let (x, tmp) = maybeMakeTemp(p, m[0], a) 1029 | r.address = x 1030 | var typ = skipTypes(m[0].typ, abstractPtrs) 1031 | if typ.kind == tyArray: 1032 | first = firstOrd(p.config, typ[0]) 1033 | if optBoundsCheck in p.options: 1034 | useMagic(p, "chckIndx") 1035 | r.res = "chckIndx($1, $2, ($3 != null ? $3.length : 0)+$2-1)-$2" % [b.res, rope(first), tmp] 1036 | elif first != 0: 1037 | r.res = "($1)-$2" % [b.res, rope(first)] 1038 | else: 1039 | r.res = b.res 1040 | r.kind = resExpr 1041 | 1042 | proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = 1043 | var ty = skipTypes(n[0].typ, abstractVarRange) 1044 | if ty.kind in {tyRef, tyPtr, tyLent, tyOwned}: ty = skipTypes(ty.lastSon, abstractVarRange) 1045 | case ty.kind 1046 | of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: 1047 | genArrayAddr(p, n, r) 1048 | of tyTuple: 1049 | genFieldAddr(p, n, r) 1050 | else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')') 1051 | r.typ = mapType(n.typ) 1052 | if r.res == nil: internalError(p.config, n.info, "genArrayAccess") 1053 | if ty.kind == tyCString: 1054 | r.res = "$1.charCodeAt($2)" % [r.address, r.res] 1055 | elif r.typ == etyBaseIndex: 1056 | if needsTemp(p, n[0]): 1057 | let tmp = p.getTemp 1058 | r.address = "($1 = $2, $1)[0]" % [tmp, r.rdLoc] 1059 | r.res = "$1[1]" % [tmp] 1060 | r.tmpLoc = tmp 1061 | else: 1062 | let x = r.rdLoc 1063 | r.address = "$1[0]" % [x] 1064 | r.res = "$1[1]" % [x] 1065 | else: 1066 | r.res = "$1[$2]" % [r.address, r.res] 1067 | r.kind = resExpr 1068 | 1069 | template isIndirect(x: PSym): bool = 1070 | let v = x 1071 | ({sfAddrTaken, sfGlobal} * v.flags != {} and {sfImportc, sfExportc} * v.flags == {} and 1072 | v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator, skConst, skTemp, skLet}) 1073 | 1074 | proc genAddr(p: PProc, n: PNode, r: var TCompRes) = 1075 | case n[0].kind 1076 | of nkSym: 1077 | let s = n[0].sym 1078 | if s.loc.r == nil: internalError(p.config, n.info, "genAddr: 3") 1079 | case s.kind 1080 | of skVar, skLet, skResult: 1081 | r.kind = resExpr 1082 | let jsType = mapType(p, n.typ) 1083 | if jsType == etyObject: 1084 | # make addr() a no-op: 1085 | r.typ = etyNone 1086 | if isIndirect(s): 1087 | r.res = s.loc.r & "[0]" 1088 | else: 1089 | r.res = s.loc.r 1090 | r.address = nil 1091 | elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex: 1092 | # for ease of code generation, we do not distinguish between 1093 | # sfAddrTaken and sfGlobal. 1094 | r.typ = etyBaseIndex 1095 | r.address = s.loc.r 1096 | r.res = rope("0") 1097 | else: 1098 | # 'var openArray' for instance produces an 'addr' but this is harmless: 1099 | gen(p, n[0], r) 1100 | #internalError(p.config, n.info, "genAddr: 4 " & renderTree(n)) 1101 | else: internalError(p.config, n.info, "genAddr: 2") 1102 | of nkCheckedFieldExpr: 1103 | genCheckedFieldOp(p, n[0], n.typ, r) 1104 | of nkDotExpr: 1105 | if mapType(p, n.typ) == etyBaseIndex: 1106 | genFieldAddr(p, n[0], r) 1107 | else: 1108 | genFieldAccess(p, n[0], r) 1109 | of nkBracketExpr: 1110 | var ty = skipTypes(n[0].typ, abstractVarRange) 1111 | if ty.kind in MappedToObject: 1112 | gen(p, n[0], r) 1113 | else: 1114 | let kindOfIndexedExpr = skipTypes(n[0][0].typ, abstractVarRange).kind 1115 | case kindOfIndexedExpr 1116 | of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: 1117 | genArrayAddr(p, n[0], r) 1118 | of tyTuple: 1119 | genFieldAddr(p, n[0], r) 1120 | else: internalError(p.config, n[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')') 1121 | of nkObjDownConv: 1122 | gen(p, n[0], r) 1123 | of nkHiddenDeref: 1124 | gen(p, n[0][0], r) 1125 | else: internalError(p.config, n[0].info, "genAddr: " & $n[0].kind) 1126 | 1127 | template attachProc(p: PProc; content: Rope; s: PSym) = p.g.code.add(content) 1128 | 1129 | template attachProc(p: PProc; s: PSym) = 1130 | let newp = genProc(p, s) 1131 | attachProc(p, newp, s) 1132 | 1133 | proc genProcForSymIfNeeded(p: PProc, s: PSym) = 1134 | if not p.g.generatedSyms.containsOrIncl(s.id): 1135 | let newp = genProc(p, s) 1136 | var owner = p 1137 | while owner != nil and owner.prc != s.owner: owner = owner.up 1138 | if owner != nil: owner.locals.add(newp) 1139 | else: attachProc(p, newp, s) 1140 | 1141 | proc genCopyForParamIfNeeded(p: PProc, n: PNode) = 1142 | let s = n.sym 1143 | if p.prc == s.owner or needsNoCopy(p, n): 1144 | return 1145 | var owner = p.up 1146 | while true: 1147 | if owner == nil: 1148 | internalError(p.config, n.info, "couldn't find the owner proc of the closed over param: " & s.name.s) 1149 | if owner.prc == s.owner: 1150 | if not owner.generatedParamCopies.containsOrIncl(s.id): 1151 | let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.r, genTypeInfo(p, s.typ)] 1152 | owner.locals.add(owner.indentLine(copy)) 1153 | return 1154 | owner = owner.up 1155 | 1156 | proc genVarInit(p: PProc, v: PSym, n: PNode) 1157 | 1158 | proc genSym(p: PProc, n: PNode, r: var TCompRes) = 1159 | var s = n.sym 1160 | case s.kind 1161 | of skVar, skLet, skParam, skTemp, skResult, skForVar: 1162 | if s.loc.r == nil: 1163 | internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) 1164 | if sfCompileTime in s.flags: 1165 | genVarInit(p, s, if s.ast != nil: s.ast else: newNodeI(nkEmpty, s.info)) 1166 | if s.kind == skParam: 1167 | genCopyForParamIfNeeded(p, n) 1168 | let k = mapType(p, s.typ) 1169 | if k == etyBaseIndex: 1170 | r.typ = etyBaseIndex 1171 | if {sfAddrTaken, sfGlobal} * s.flags != {}: 1172 | if isIndirect(s): 1173 | r.address = "$1[0][0]" % [s.loc.r] 1174 | r.res = "$1[0][1]" % [s.loc.r] 1175 | else: 1176 | r.address = "$1[0]" % [s.loc.r] 1177 | r.res = "$1[1]" % [s.loc.r] 1178 | else: 1179 | r.address = s.loc.r 1180 | r.res = s.loc.r & "_Idx" 1181 | elif isIndirect(s): 1182 | r.res = "$1[0]" % [s.loc.r] 1183 | else: 1184 | r.res = s.loc.r 1185 | of skConst: 1186 | genConstant(p, s) 1187 | if s.loc.r == nil: 1188 | internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) 1189 | r.res = s.loc.r 1190 | of skProc, skFunc, skConverter, skMethod: 1191 | if sfCompileTime in s.flags: 1192 | localError(p.config, n.info, "request to generate code for .compileTime proc: " & s.name.s) 1193 | discard mangleName(p.module, s) 1194 | r.res = s.loc.r 1195 | if lfNoDecl in s.loc.flags or s.magic != mNone or 1196 | {sfImportc, sfInfixCall} * s.flags != {}: 1197 | discard 1198 | elif s.kind == skMethod and s.getBody.kind == nkEmpty: 1199 | # we cannot produce code for the dispatcher yet: 1200 | discard 1201 | elif sfForward in s.flags: 1202 | p.g.forwarded.add(s) 1203 | else: 1204 | genProcForSymIfNeeded(p, s) 1205 | else: 1206 | if s.loc.r == nil: 1207 | internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) 1208 | r.res = s.loc.r 1209 | r.kind = resVal 1210 | 1211 | proc genDeref(p: PProc, n: PNode, r: var TCompRes) = 1212 | let it = n[0] 1213 | let t = mapType(p, it.typ) 1214 | if t == etyObject: 1215 | gen(p, it, r) 1216 | else: 1217 | var a: TCompRes 1218 | gen(p, it, a) 1219 | r.kind = a.kind 1220 | r.typ = mapType(p, n.typ) 1221 | if r.typ == etyBaseIndex: 1222 | let tmp = p.getTemp 1223 | r.address = "($1 = $2, $1)[0]" % [tmp, a.rdLoc] 1224 | r.res = "$1[1]" % [tmp] 1225 | r.tmpLoc = tmp 1226 | elif a.typ == etyBaseIndex: 1227 | if a.tmpLoc != nil: 1228 | r.tmpLoc = a.tmpLoc 1229 | r.res = a.rdLoc 1230 | else: 1231 | internalError(p.config, n.info, "genDeref") 1232 | 1233 | template genArgNoParam(p: PProc, n: PNode, r: var TCompRes) = 1234 | var a: TCompRes 1235 | gen(p, n, a) 1236 | if a.typ == etyBaseIndex: 1237 | r.res.add(a.address) 1238 | r.res.add(", ") 1239 | r.res.add(a.res) 1240 | else: r.res.add(a.res) 1241 | 1242 | proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes; emitted: ptr int = nil) = 1243 | var a: TCompRes 1244 | gen(p, n, a) 1245 | if skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs} and 1246 | a.typ == etyBaseIndex: 1247 | r.res.add("$1[$2]" % [a.address, a.res]) 1248 | elif a.typ == etyBaseIndex: 1249 | r.res.add(a.address) 1250 | r.res.add(", ") 1251 | r.res.add(a.res) 1252 | if emitted != nil: inc emitted[] 1253 | elif n.typ.kind in {tyVar, tyPtr, tyRef, tyLent, tyOwned} and 1254 | n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex: 1255 | # this fixes bug #5608: 1256 | let tmp = getTemp(p) 1257 | r.res.add("($1 = $2, $1[0]), $1[1]" % [tmp, a.rdLoc]) 1258 | if emitted != nil: inc emitted[] 1259 | else: 1260 | r.res.add(a.res) 1261 | 1262 | proc genArgs(p: PProc, n: PNode, r: var TCompRes; start=1) = 1263 | r.res.add("(") 1264 | var hasArgs = false 1265 | 1266 | var typ = skipTypes(n[0].typ, abstractInst) 1267 | assert(typ.kind == tyProc) 1268 | assert(typ.len == typ.n.len) 1269 | var emitted = start-1 1270 | 1271 | for i in start..= n.len: 1292 | globalError(p.config, n.info, "wrong importcpp pattern; expected parameter at position " & $i & 1293 | " but got only: " & $(n.len-1)) 1294 | let it = n[i] 1295 | var paramType: PNode = nil 1296 | if i < typ.len: 1297 | assert typ.n[i].kind == nkSym 1298 | paramType = typ.n[i] 1299 | if paramType.typ.isCompileTimeOnly: return 1300 | if paramType.isNil: genArgNoParam(p, it, r) 1301 | else: genArg(p, it, paramType.sym, r) 1302 | inc generated 1303 | 1304 | proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType; 1305 | r: var TCompRes) = 1306 | var i = 0 1307 | var j = 1 1308 | r.kind = resExpr 1309 | while i < pat.len: 1310 | case pat[i] 1311 | of '@': 1312 | var generated = 0 1313 | for k in j.. 0: r.res.add(", ") 1315 | genOtherArg(p, n, k, typ, generated, r) 1316 | inc i 1317 | of '#': 1318 | var generated = 0 1319 | genOtherArg(p, n, j, typ, generated, r) 1320 | inc j 1321 | inc i 1322 | of '\31': 1323 | # unit separator 1324 | r.res.add("#") 1325 | inc i 1326 | of '\29': 1327 | # group separator 1328 | r.res.add("@") 1329 | inc i 1330 | else: 1331 | let start = i 1332 | while i < pat.len: 1333 | if pat[i] notin {'@', '#', '\31', '\29'}: inc i else: break 1334 | if i - 1 >= start: r.res.add(substr(pat, start, i - 1)) 1335 | 1336 | proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = 1337 | # don't call '$' here for efficiency: 1338 | let f = n[0].sym 1339 | if f.loc.r == nil: f.loc.r = mangleName(p.module, f) 1340 | if sfInfixCall in f.flags: 1341 | let pat = n[0].sym.loc.r.data 1342 | internalAssert p.config, pat.len > 0 1343 | if pat.contains({'#', '(', '@'}): 1344 | var typ = skipTypes(n[0].typ, abstractInst) 1345 | assert(typ.kind == tyProc) 1346 | genPatternCall(p, n, pat, typ, r) 1347 | return 1348 | if n.len != 1: 1349 | gen(p, n[1], r) 1350 | if r.typ == etyBaseIndex: 1351 | if r.address == nil: 1352 | globalError(p.config, n.info, "cannot invoke with infix syntax") 1353 | r.res = "$1[$2]" % [r.address, r.res] 1354 | r.address = nil 1355 | r.typ = etyNone 1356 | r.res.add(".") 1357 | var op: TCompRes 1358 | gen(p, n[0], op) 1359 | r.res.add(op.res) 1360 | genArgs(p, n, r, 2) 1361 | 1362 | proc genCall(p: PProc, n: PNode, r: var TCompRes) = 1363 | gen(p, n[0], r) 1364 | genArgs(p, n, r) 1365 | if n.typ != nil: 1366 | let t = mapType(n.typ) 1367 | if t == etyBaseIndex: 1368 | let tmp = p.getTemp 1369 | r.address = "($1 = $2, $1)[0]" % [tmp, r.rdLoc] 1370 | r.res = "$1[1]" % [tmp] 1371 | r.tmpLoc = tmp 1372 | r.typ = t 1373 | 1374 | proc genEcho(p: PProc, n: PNode, r: var TCompRes) = 1375 | let n = n[1].skipConv 1376 | internalAssert p.config, n.kind == nkBracket 1377 | r.res.add("print(") 1378 | for i in 0.. 0: r.res.add(", ") 1382 | genArgNoParam(p, it, r) 1383 | r.res.add(")") 1384 | r.kind = resExpr 1385 | 1386 | proc putToSeq(s: string, indirect: bool): Rope = 1387 | result = rope(s) 1388 | if indirect: result = "[$1]" % [result] 1389 | 1390 | proc createVar(p: PProc, typ: PType, indirect: bool): Rope 1391 | proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output: var Rope) = 1392 | case rec.kind 1393 | of nkRecList: 1394 | for i in 0.. 0: output.add(", ") 1405 | output.addf("$#: ", [mangleName(p.module, rec.sym)]) 1406 | output.add(createVar(p, rec.sym.typ, false)) 1407 | else: internalError(p.config, rec.info, "createRecordVarAux") 1408 | 1409 | proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: var Rope) = 1410 | var t = typ 1411 | if objHasTypeField(t): 1412 | if output.len > 0: output.add(", ") 1413 | output.addf("m_type: $1", [genTypeInfo(p, t)]) 1414 | while t != nil: 1415 | t = t.skipTypes(skipPtrs) 1416 | createRecordVarAux(p, t.n, excludedFieldIDs, output) 1417 | t = t[0] 1418 | 1419 | proc arrayTypeForElemType(typ: PType): string = 1420 | # XXX This should also support tyEnum and tyBool 1421 | case typ.kind 1422 | of tyInt, tyInt32: "Int32Array" 1423 | of tyInt16: "Int16Array" 1424 | of tyInt8: "Int8Array" 1425 | of tyUInt, tyUInt32: "Uint32Array" 1426 | of tyUInt16: "Uint16Array" 1427 | of tyUInt8: "Uint8Array" 1428 | of tyFloat32: "Float32Array" 1429 | of tyFloat64, tyFloat: "Float64Array" 1430 | else: "" 1431 | 1432 | proc createVar(p: PProc, typ: PType, indirect: bool): Rope = 1433 | var t = skipTypes(typ, abstractInst) 1434 | case t.kind 1435 | of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: 1436 | result = rope"0" 1437 | of tyFloat..tyFloat128: 1438 | result = rope"0.0" 1439 | of tyRange, tyGenericInst, tyAlias, tySink, tyOwned: 1440 | result = createVar(p, lastSon(typ), indirect) 1441 | of tySet: 1442 | result = rope"{}" 1443 | of tyBool: 1444 | result = rope"False" 1445 | of tyNil: 1446 | result = rope"None" 1447 | of tyArray: 1448 | result = rope"[]" 1449 | of tyTuple: 1450 | result = rope("collections.namedtuple('_', [") 1451 | for i in 0.. 0: result.add(", ") 1456 | result.addf("$2, ", [createVar(p, t[i], false)]) 1457 | result.add(")") 1458 | if indirect: result = "[$1]" % [result] 1459 | of tyObject: 1460 | var initList: Rope 1461 | createObjInitList(p, t, initIntSet(), initList) 1462 | result = ("{$1}") % [initList] 1463 | if indirect: result = "$1" % [result] 1464 | of tyVar, tyPtr, tyLent, tyRef, tyPointer: 1465 | result = rope"None" 1466 | of tySequence, tyOpt, tyProc: 1467 | result = rope"None" 1468 | of tyString, tyCString: 1469 | result = rope"str()" 1470 | of tyStatic: 1471 | if t.n != nil: 1472 | result = createVar(p, lastSon t, indirect) 1473 | else: 1474 | internalError(p.config, "createVar: " & $t.kind) 1475 | result = nil 1476 | else: 1477 | internalError(p.config, "createVar: " & $t.kind) 1478 | result = nil 1479 | 1480 | template returnType: untyped = ~"" 1481 | 1482 | proc genVarInit(p: PProc, v: PSym, n: PNode) = 1483 | var 1484 | a: TCompRes 1485 | s: Rope 1486 | varCode: string 1487 | varName = mangleName(p.module, v) 1488 | varCode = if v.constraint.isNil: "$2" else: v.constraint.strVal 1489 | case n.kind 1490 | of nkEmpty, nkNilLit: 1491 | lineF(p, "$1: None = None # type: None$n", [varName]) 1492 | of nkIntLit, nkInt8Lit, nkInt16Lit, nkInt32Lit, nkInt64Lit, nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit: 1493 | lineF(p, "$1: Int = 0 # type: Int$n", [varName]) 1494 | of nkFloatLit, nkFloat32Lit, nkFloat64Lit, nkFloat128Lit: 1495 | lineF(p, "$1: Float = 0.0 # type: Float$n", [varName]) 1496 | of nkStrLit, nkRStrLit, nkTripleStrLit: 1497 | lineF(p, "$1: String = str() # type: String$n", [varName]) # str() used to skip "" Vs '' style war. 1498 | of nkTableConstr: 1499 | lineF(p, "$1: Dict = {} # type: Dict$n", [varName]) 1500 | of nkTupleTy, nkTupleClassTy, nkTupleConstr: 1501 | lineF(p, "$1: Tuple = tuple() # type: Tuple$n", [varName]) 1502 | else: 1503 | s = a.res 1504 | line(p, runtimeFormat(varCode & ": $4 = $3 # type: $4$n", [returnType, v.loc.r, s, rope($v.typ)])) 1505 | 1506 | proc genVarStmt(p: PProc, n: PNode) = 1507 | for i in 0.. 0: r.res.add(", ") 1592 | gen(p, it[0], a) 1593 | gen(p, it[1], b) 1594 | r.res.add("$# => $#" % [a.rdLoc, b.rdLoc]) 1595 | else: 1596 | localError(p.config, it.info, "'toArray' needs tuple constructors") 1597 | else: 1598 | localError(p.config, x.info, "'toArray' needs an array literal") 1599 | r.res.add(")") 1600 | 1601 | proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = nil) = 1602 | useMagic(p, magic) 1603 | r.res.add(magic & "(") 1604 | var a: TCompRes 1605 | 1606 | gen(p, n[1], a) 1607 | if magic == "reprAny": 1608 | # the pointer argument in reprAny is expandend to 1609 | # (pointedto, pointer), so we need to fill it 1610 | if a.address.isNil: 1611 | r.res.add(a.res) 1612 | r.res.add(", None") 1613 | else: 1614 | r.res.add("$1, $2" % [a.address, a.res]) 1615 | else: 1616 | r.res.add(a.res) 1617 | 1618 | if not typ.isNil: 1619 | r.res.add(", ") 1620 | r.res.add(typ) 1621 | r.res.add(")") 1622 | 1623 | proc genRepr(p: PProc, n: PNode, r: var TCompRes) = 1624 | let t = skipTypes(n[1].typ, abstractVarRange) 1625 | case t.kind: 1626 | of tyInt..tyInt64, tyUInt..tyUInt64: 1627 | genReprAux(p, n, r, "reprInt") 1628 | of tyChar: 1629 | genReprAux(p, n, r, "reprChar") 1630 | of tyBool: 1631 | genReprAux(p, n, r, "reprBool") 1632 | of tyFloat..tyFloat128: 1633 | genReprAux(p, n, r, "reprFloat") 1634 | of tyString: 1635 | genReprAux(p, n, r, "reprStr") 1636 | of tyEnum, tyOrdinal: 1637 | genReprAux(p, n, r, "reprEnum", genTypeInfo(p, t)) 1638 | of tySet: 1639 | genReprAux(p, n, r, "reprSet", genTypeInfo(p, t)) 1640 | of tyEmpty, tyVoid: 1641 | localError(p.config, n.info, "'repr' doesn't support 'void' type") 1642 | of tyPointer: 1643 | genReprAux(p, n, r, "reprPointer") 1644 | of tyOpenArray, tyVarargs: 1645 | genReprAux(p, n, r, "reprJSONStringify") 1646 | else: 1647 | genReprAux(p, n, r, "reprAny", genTypeInfo(p, t)) 1648 | 1649 | proc genOf(p: PProc, n: PNode, r: var TCompRes) = 1650 | var x: TCompRes 1651 | let t = skipTypes(n[2].typ, 1652 | abstractVarRange+{tyRef, tyPtr, tyLent, tyTypeDesc, tyOwned}) 1653 | gen(p, n[1], x) 1654 | if tfFinal in t.flags: 1655 | r.res = "($1.m_type == $2)" % [x.res, genTypeInfo(p, t)] 1656 | else: 1657 | useMagic(p, "isObj") 1658 | r.res = "isObj($1.m_type, $2)" % [x.res, genTypeInfo(p, t)] 1659 | r.kind = resExpr 1660 | 1661 | proc genDefault(p: PProc, n: PNode; r: var TCompRes) = 1662 | r.res = createVar(p, n.typ, indirect = false) 1663 | r.kind = resExpr 1664 | 1665 | proc genReset(p: PProc, n: PNode) = 1666 | var x: TCompRes 1667 | useMagic(p, "genericReset") 1668 | gen(p, n[1], x) 1669 | if x.typ == etyBaseIndex: 1670 | lineF(p, "$1 = None, $2 = 0$n", [x.address, x.res]) 1671 | else: 1672 | let (a, tmp) = maybeMakeTemp(p, n[1], x) 1673 | lineF(p, "$1 = genericReset($3, $2)$n", [a, 1674 | genTypeInfo(p, n[1].typ), tmp]) 1675 | 1676 | proc genMagic(p: PProc, n: PNode, r: var TCompRes) = 1677 | var 1678 | a: TCompRes 1679 | line, filen: Rope 1680 | var op = n[0].sym.magic 1681 | case op 1682 | of mOr: genOr(p, n[1], n[2], r) 1683 | of mAnd: genAnd(p, n[1], n[2], r) 1684 | of mAddI..mStrToStr: arith(p, n, r, op) 1685 | of mRepr: genRepr(p, n, r) 1686 | of mSwap: genSwap(p, n) 1687 | of mUnaryLt: 1688 | # XXX: range checking? 1689 | if not (optOverflowCheck in p.options): unaryExpr(p, n, r, "", "$1 - 1") 1690 | else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)") 1691 | of mAppendStrCh: 1692 | binaryExpr(p, n, r, "addChar", 1693 | "if ($1 != null) { addChar($3, $2); } else { $3 = [$2]; }") 1694 | of mAppendStrStr: 1695 | var lhs, rhs: TCompRes 1696 | gen(p, n[1], lhs) 1697 | gen(p, n[2], rhs) 1698 | 1699 | let rhsIsLit = n[2].kind in nkStrKinds 1700 | let (a, tmp) = maybeMakeTemp(p, n[1], lhs) 1701 | if skipTypes(n[1].typ, abstractVarRange).kind == tyCString: 1702 | r.res = "if ($1 != null) { $4 += $2; } else { $4 = $2$3; }" % [ 1703 | a, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()", tmp] 1704 | else: 1705 | r.res = "if ($1 != null) { $4 = ($4).concat($2); } else { $4 = $2$3; }" % [ 1706 | lhs.rdLoc, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()", tmp] 1707 | r.kind = resExpr 1708 | of mAppendSeqElem: 1709 | var x, y: TCompRes 1710 | gen(p, n[1], x) 1711 | gen(p, n[2], y) 1712 | let (a, tmp) = maybeMakeTemp(p, n[1], x) 1713 | if mapType(n[2].typ) == etyBaseIndex: 1714 | let c = "[$1, $2]" % [y.address, y.res] 1715 | r.res = "if ($1 != null) { $3.push($2); } else { $3 = [$2]; }" % [a, c, tmp] 1716 | elif needsNoCopy(p, n[2]): 1717 | r.res = "if ($1 != null) { $3.push($2); } else { $3 = [$2]; }" % [a, y.rdLoc, tmp] 1718 | else: 1719 | useMagic(p, "nimCopy") 1720 | let c = getTemp(p, defineInLocals=false) 1721 | lineF(p, "var $1 = nimCopy(null, $2, $3);$n", 1722 | [c, y.rdLoc, genTypeInfo(p, n[2].typ)]) 1723 | r.res = "if ($1 != null) { $3.push($2); } else { $3 = [$2]; }" % [a, c, tmp] 1724 | r.kind = resExpr 1725 | of mConStrStr: 1726 | genConStrStr(p, n, r) 1727 | of mEqStr: 1728 | binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)") 1729 | of mLeStr: 1730 | binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)") 1731 | of mLtStr: 1732 | binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)") 1733 | of mIsNil: 1734 | # we want to accept undefined, so we == 1735 | if mapType(n[1].typ) != etyBaseIndex: 1736 | unaryExpr(p, n, r, "", "bool($1 == None)") 1737 | else: 1738 | var x: TCompRes 1739 | gen(p, n[1], x) 1740 | r.res = "bool($# == None or $# == 0)" % [x.address, x.res] 1741 | of mEnumToStr: genRepr(p, n, r) 1742 | of mNew, mNewFinalize: genNew(p, n) 1743 | of mChr: gen(p, n[1], r) 1744 | of mArrToSeq: 1745 | if needsNoCopy(p, n[1]): 1746 | gen(p, n[1], r) 1747 | else: 1748 | var x: TCompRes 1749 | gen(p, n[1], x) 1750 | useMagic(p, "nimCopy") 1751 | r.res = "nimCopy(null, $1, $2)" % [x.rdLoc, genTypeInfo(p, n.typ)] 1752 | of mDestroy: discard "ignore calls to the default destructor" 1753 | of mOrd: genOrd(p, n, r) 1754 | of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray: 1755 | unaryExpr(p, n, r, "", "($1 != null ? $2.length : 0)") 1756 | of mXLenStr, mXLenSeq: 1757 | unaryExpr(p, n, r, "", "len($1)") 1758 | of mHigh: 1759 | unaryExpr(p, n, r, "", "($1 != null ? ($2.length-1) : -1)") 1760 | of mInc: 1761 | if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}: 1762 | binaryUintExpr(p, n, r, "+", true) 1763 | else: 1764 | if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2") 1765 | else: binaryExpr(p, n, r, "addInt", "$1 = addInt($3, $2)") 1766 | of ast.mDec: 1767 | if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}: 1768 | binaryUintExpr(p, n, r, "-", true) 1769 | else: 1770 | if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2") 1771 | else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)") 1772 | of mSetLengthStr: 1773 | binaryExpr(p, n, r, "mnewString", "($1 == null ? $3 = mnewString($2) : $3.length = $2)") 1774 | of mSetLengthSeq: 1775 | var x, y: TCompRes 1776 | gen(p, n[1], x) 1777 | gen(p, n[2], y) 1778 | let t = skipTypes(n[1].typ, abstractVar)[0] 1779 | let (a, tmp) = maybeMakeTemp(p, n[1], x) 1780 | let (b, tmp2) = maybeMakeTemp(p, n[2], y) 1781 | r.res = """if ($1 === null) $4 = []; 1782 | if ($4.length < $2) { for (var i=$4.length;i<$5;++i) $4.push($3); } 1783 | else { $4.length = $5; }""" % [a, b, createVar(p, t, false), tmp, tmp2] 1784 | r.kind = resExpr 1785 | of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)") 1786 | of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)") 1787 | of mLeSet: binaryExpr(p, n, r, "SetLe", "SetLe($1, $2)") 1788 | of mEqSet: binaryExpr(p, n, r, "SetEq", "SetEq($1, $2)") 1789 | of mMulSet: binaryExpr(p, n, r, "SetMul", "SetMul($1, $2)") 1790 | of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)") 1791 | of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)") 1792 | of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true") 1793 | of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]") 1794 | of mInSet: 1795 | binaryExpr(p, n, r, "", "($1[$2] != undefined)") 1796 | of mNewSeq: genNewSeq(p, n) 1797 | of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]") 1798 | of mOf: genOf(p, n, r) 1799 | of mDefault: genDefault(p, n, r) 1800 | of mReset: genReset(p, n) 1801 | of mEcho: genEcho(p, n, r) 1802 | of mNLen..mNError, mSlurp, mStaticExec: 1803 | localError(p.config, n.info, errXMustBeCompileTime % n[0].sym.name.s) 1804 | of mCopyStr: 1805 | binaryExpr(p, n, r, "", "($1.slice($2))") 1806 | of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)") 1807 | of mNewStringOfCap: 1808 | unaryExpr(p, n, r, "mnewString", "mnewString(0)") 1809 | of mDotDot: 1810 | genProcForSymIfNeeded(p, n[0].sym) 1811 | genCall(p, n, r) 1812 | of mParseBiggestFloat: 1813 | useMagic(p, "nimParseBiggestFloat") 1814 | genCall(p, n, r) 1815 | of mSlice: 1816 | # arr.slice([begin[, end]]): 'end' is exclusive 1817 | var x, y, z: TCompRes 1818 | gen(p, n[1], x) 1819 | gen(p, n[2], y) 1820 | gen(p, n[3], z) 1821 | r.res = "($1.slice($2, $3+1))" % [x.rdLoc, y.rdLoc, z.rdLoc] 1822 | r.kind = resExpr 1823 | else: 1824 | genCall(p, n, r) 1825 | #else internalError(p.config, e.info, 'genMagic: ' + magicToStr[op]); 1826 | 1827 | proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = 1828 | var 1829 | a, b: TCompRes 1830 | useMagic(p, "setConstr") 1831 | r.res = rope("setConstr(") 1832 | r.kind = resExpr 1833 | for i in 0.. 0: r.res.add(", ") 1835 | var it = n[i] 1836 | if it.kind == nkRange: 1837 | gen(p, it[0], a) 1838 | gen(p, it[1], b) 1839 | r.res.addf("[$1, $2]", [a.res, b.res]) 1840 | else: 1841 | gen(p, it, a) 1842 | r.res.add(a.res) 1843 | r.res.add(")") 1844 | # emit better code for constant sets: 1845 | if isDeepConstExpr(n): 1846 | inc(p.g.unique) 1847 | let tmp = rope("ConstSet") & rope(p.g.unique) 1848 | p.g.constants.addf("var $1 = $2;$n", [tmp, r.res]) 1849 | r.res = tmp 1850 | 1851 | proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = 1852 | var a: TCompRes 1853 | r.res = rope("[") 1854 | r.kind = resExpr 1855 | for i in 0.. 0: r.res.add(", ") 1857 | gen(p, n[i], a) 1858 | if a.typ == etyBaseIndex: 1859 | r.res.addf("[$1, $2]", [a.address, a.res]) 1860 | else: 1861 | if not needsNoCopy(p, n[i]): 1862 | let typ = n[i].typ.skipTypes(abstractInst) 1863 | useMagic(p, "nimCopy") 1864 | a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] 1865 | r.res.add(a.res) 1866 | r.res.add("]") 1867 | 1868 | proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = 1869 | var a: TCompRes 1870 | r.res = rope("{") 1871 | r.kind = resExpr 1872 | for i in 0.. 0: r.res.add(", ") 1874 | var it = n[i] 1875 | if it.kind == nkExprColonExpr: it = it[1] 1876 | gen(p, it, a) 1877 | let typ = it.typ.skipTypes(abstractInst) 1878 | if a.typ == etyBaseIndex: 1879 | r.res.addf("Field$#: [$#, $#]", [i.rope, a.address, a.res]) 1880 | else: 1881 | if not needsNoCopy(p, it): 1882 | useMagic(p, "nimCopy") 1883 | a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] 1884 | r.res.addf("Field$#: $#", [i.rope, a.res]) 1885 | r.res.add("}") 1886 | 1887 | proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = 1888 | var a: TCompRes 1889 | r.kind = resExpr 1890 | var initList : Rope 1891 | var fieldIDs = initIntSet() 1892 | for i in 1.. 1: initList.add(", ") 1894 | var it = n[i] 1895 | internalAssert p.config, it.kind == nkExprColonExpr 1896 | let val = it[1] 1897 | gen(p, val, a) 1898 | var f = it[0].sym 1899 | if f.loc.r == nil: f.loc.r = mangleName(p.module, f) 1900 | fieldIDs.incl(f.id) 1901 | 1902 | let typ = val.typ.skipTypes(abstractInst) 1903 | if a.typ == etyBaseIndex: 1904 | initList.addf("$#: [$#, $#]", [f.loc.r, a.address, a.res]) 1905 | else: 1906 | if not needsNoCopy(p, val): 1907 | useMagic(p, "nimCopy") 1908 | a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] 1909 | initList.addf("$#: $#", [f.loc.r, a.res]) 1910 | let t = skipTypes(n.typ, abstractInst + skipPtrs) 1911 | createObjInitList(p, t, fieldIDs, initList) 1912 | r.res = ("{$1}") % [initList] 1913 | 1914 | proc genConv(p: PProc, n: PNode, r: var TCompRes) = 1915 | var dest = skipTypes(n.typ, abstractVarRange) 1916 | var src = skipTypes(n[1].typ, abstractVarRange) 1917 | gen(p, n[1], r) 1918 | if dest.kind == src.kind: 1919 | # no-op conversion 1920 | return 1921 | case dest.kind: 1922 | of tyBool: 1923 | r.res = "bool($1)" % [r.res] 1924 | r.kind = resExpr 1925 | of tyInt: 1926 | r.res = "int($1)" % [r.res] 1927 | of tyFloat: 1928 | r.res = "float($1)" % [r.res] 1929 | of tyString: 1930 | r.res = "str($1)" % [r.res] 1931 | else: 1932 | # TODO: What types must we handle here? 1933 | discard 1934 | 1935 | template upConv(p: PProc, n: PNode, r: var TCompRes) = gen(p, n[0], r) # XXX 1936 | 1937 | proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) = 1938 | var a, b: TCompRes 1939 | gen(p, n[0], r) 1940 | if optRangeCheck notin p.options or (skipTypes(n.typ, abstractVar).kind in {tyUInt..tyUInt64} and 1941 | checkUnsignedConversions notin p.config.legacyFeatures): 1942 | discard "XXX maybe emit masking instructions here" 1943 | else: 1944 | gen(p, n[1], a) 1945 | gen(p, n[2], b) 1946 | useMagic(p, "chckRange") 1947 | r.res = "chckRange($1, $2, $3)" % [r.res, a.res, b.res] 1948 | r.kind = resExpr 1949 | 1950 | proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) = 1951 | # we do an optimization here as this is likely to slow down 1952 | # much of the code otherwise: 1953 | if n[0].kind == nkCStringToString: 1954 | gen(p, n[0][0], r) 1955 | else: 1956 | gen(p, n[0], r) 1957 | if r.res == nil: internalError(p.config, n.info, "convStrToCStr") 1958 | r.res = "str($1)" % [r.res] 1959 | r.kind = resExpr 1960 | 1961 | proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) = 1962 | # we do an optimization here as this is likely to slow down 1963 | # much of the code otherwise: 1964 | if n[0].kind == nkStringToCString: 1965 | gen(p, n[0][0], r) 1966 | else: 1967 | gen(p, n[0], r) 1968 | if r.res == nil: internalError(p.config, n.info, "convCStrToStr") 1969 | r.res = "str($1)" % [r.res] 1970 | r.kind = resExpr 1971 | 1972 | proc genReturnStmt(p: PProc, n: PNode) = 1973 | if p.procDef == nil: internalError(p.config, n.info, "genReturnStmt") 1974 | if n[0].kind != nkEmpty: genStmt(p, n[0]) else: genLineDir(p, n) 1975 | 1976 | proc frameCreate(p: PProc; procname, filename: Rope): Rope = 1977 | const frameFmt = "F = {procname: $1, prev: framePtr, filename: $2, line: 0}$n" 1978 | result = p.indentLine(frameFmt % [procname, filename]) 1979 | result.add p.indentLine(ropes.`%`("framePtr = F$n", [])) 1980 | 1981 | proc frameDestroy(p: PProc): Rope = 1982 | result = p.indentLine rope(("framePtr = F.prev;") & "\L") 1983 | 1984 | proc genProcBody(p: PProc, prc: PSym): Rope = 1985 | if hasFrameInfo(p): 1986 | result = frameCreate(p, makeJSString(prc.owner.name.s & '.' & prc.name.s), makeJSString(toFilename(p.config, prc.info))) 1987 | else: 1988 | result = nil 1989 | result.add(p.body) 1990 | if hasFrameInfo(p): result.add(frameDestroy(p)) 1991 | 1992 | template optionalLine(p: Rope): Rope = 1993 | if p == nil: nil else: p & "\L" 1994 | 1995 | proc genProc(oldProc: PProc, prc: PSym): Rope = 1996 | var resultSym: PSym 1997 | var a: TCompRes 1998 | var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options) 1999 | p.up = oldProc 2000 | var returnStmt: Rope = nil 2001 | var resultAsgn: Rope = nil 2002 | var name = mangleName(p.module, prc) 2003 | let header = generateHeader(p, prc.typ) 2004 | if prc.typ[0] != nil and sfPure notin prc.flags: 2005 | resultSym = prc.ast[resultPos].sym 2006 | let mname = mangleName(p.module, resultSym) 2007 | if not isIndirect(resultSym) and resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef, tyOwned} and mapType(p, resultSym.typ) == etyBaseIndex: 2008 | resultAsgn = p.indentLine(("$# = None$n") % [mname]) 2009 | resultAsgn.add p.indentLine("$#_Idx = 0$n" % [mname]) 2010 | else: 2011 | let resVar = createVar(p, resultSym.typ, isIndirect(resultSym)) 2012 | resultAsgn = p.indentLine(("$# = $#$n") % [mname, resVar]) 2013 | gen(p, prc.ast[resultPos], a) 2014 | if mapType(p, resultSym.typ) == etyBaseIndex: 2015 | returnStmt = "return [$#, $#]$n" % [a.address, a.res] 2016 | else: 2017 | returnStmt = "return $#$n" % [a.res] 2018 | 2019 | var transformedBody = transformBody(oldProc.module.graph, prc, cache = false) 2020 | if sfInjectDestructors in prc.flags: 2021 | transformedBody = injectDestructorCalls(oldProc.module.graph, prc, transformedBody) 2022 | 2023 | genStmt(p, transformedBody) 2024 | 2025 | var def: Rope 2026 | if not prc.constraint.isNil: 2027 | def = runtimeFormat(prc.constraint.strVal & " {$n$#$#$#$#$#", 2028 | [ returnType, 2029 | name, 2030 | header, 2031 | optionalLine(p.globals), 2032 | optionalLine(p.locals), 2033 | optionalLine(resultAsgn), 2034 | optionalLine(genProcBody(p, prc)), 2035 | optionalLine(p.indentLine(returnStmt))]) 2036 | else: 2037 | result = ~"\L" 2038 | def = "def $#($#):$n$#$#$#$#$#" % 2039 | [ name, 2040 | header, 2041 | optionalLine(p.globals), 2042 | optionalLine(p.locals), 2043 | optionalLine(resultAsgn), 2044 | optionalLine(genProcBody(p, prc)), 2045 | optionalLine(p.indentLine(returnStmt))] 2046 | 2047 | dec p.extraIndent 2048 | result.add p.indentLine(def) 2049 | result.add p.indentLine(~"$n") 2050 | 2051 | proc genStmt(p: PProc, n: PNode) = 2052 | var r: TCompRes 2053 | gen(p, n, r) 2054 | if r.res != nil: lineF(p, "$#$n", [r.res]) 2055 | 2056 | template genPragma(p: PProc, n: PNode) = 2057 | for it in n.sons: 2058 | case whichPragma(it) 2059 | of wEmit: genAsmOrEmitStmt(p, it[1]) 2060 | else: discard 2061 | 2062 | proc genCast(p: PProc, n: PNode, r: var TCompRes) = 2063 | var dest = skipTypes(n.typ, abstractVarRange) 2064 | var src = skipTypes(n[1].typ, abstractVarRange) 2065 | gen(p, n[1], r) 2066 | if dest.kind == src.kind: 2067 | # no-op conversion 2068 | return 2069 | let toInt = (dest.kind in tyInt..tyInt32) 2070 | let toUint = (dest.kind in tyUInt..tyUInt32) 2071 | let fromInt = (src.kind in tyInt..tyInt32) 2072 | let fromUint = (src.kind in tyUInt..tyUInt32) 2073 | 2074 | if toUint and (fromInt or fromUint): 2075 | let trimmer = unsignedTrimmer(dest.size) 2076 | r.res = "($1 $2)" % [r.res, trimmer] 2077 | elif toInt: 2078 | if fromInt: 2079 | let trimmer = unsignedTrimmer(dest.size) 2080 | r.res = "($1 $2)" % [r.res, trimmer] 2081 | elif fromUint: 2082 | if src.size == 4 and dest.size == 4: 2083 | # XXX prevent multi evaluations 2084 | r.res = "int($1)" % [r.res] 2085 | else: 2086 | let trimmer = unsignedTrimmer(dest.size) 2087 | let minuend = case dest.size 2088 | of 1: "0xfe" 2089 | of 2: "0xfffe" 2090 | of 4: "0xfffffffe" 2091 | else: "" 2092 | r.res = "($1 - ($2 $3))" % [rope minuend, r.res, trimmer] 2093 | elif (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer: 2094 | r.address = r.res 2095 | r.res = ~"None" 2096 | r.typ = etyBaseIndex 2097 | elif (dest.kind == tyPtr and mapType(p, dest) == etyObject) and src.kind == tyPointer: 2098 | r.res = r.address 2099 | r.typ = etyObject 2100 | 2101 | proc gen(p: PProc, n: PNode, r: var TCompRes) = 2102 | r.typ = etyNone 2103 | if r.kind != resCallee: r.kind = resNone 2104 | #r.address = nil 2105 | r.res = nil 2106 | case n.kind 2107 | of nkSym: 2108 | genSym(p, n, r) 2109 | of nkCharLit..nkUInt64Lit: 2110 | if n.typ.kind == tyBool: 2111 | r.res = if n.intVal == 0: rope"False" else: rope"True" 2112 | else: 2113 | r.res = rope(n.intVal) 2114 | r.kind = resExpr 2115 | of nkNilLit: 2116 | if isEmptyType(n.typ): 2117 | discard 2118 | elif mapType(p, n.typ) == etyBaseIndex: 2119 | r.typ = etyBaseIndex 2120 | r.address = rope"None" 2121 | r.res = rope"0" 2122 | r.kind = resExpr 2123 | else: 2124 | r.res = rope"None" 2125 | r.kind = resExpr 2126 | of nkStrLit..nkTripleStrLit: 2127 | if skipTypes(n.typ, abstractVarRange).kind == tyString: 2128 | if n.strVal.len != 0: 2129 | r.res = "str($1)" % [makeJSString(n.strVal)] 2130 | else: 2131 | r.res = rope"str()" 2132 | else: 2133 | r.res = makeJSString(n.strVal, false) 2134 | r.kind = resExpr 2135 | of nkFloatLit..nkFloat64Lit: 2136 | let f = n.floatVal 2137 | case classify(f) 2138 | of fcNan: 2139 | r.res = rope"NaN" 2140 | of fcNegZero: 2141 | r.res = rope"-0.0" 2142 | of fcZero: 2143 | r.res = rope"0.0" 2144 | of fcInf: 2145 | r.res = rope"float('inf')" 2146 | of fcNegInf: 2147 | r.res = rope"float('-inf')" 2148 | else: r.res = rope(f.toStrMaxPrecision) 2149 | r.kind = resExpr 2150 | of nkCallKinds: 2151 | if isEmptyType(n.typ): genLineDir(p, n) 2152 | if (n[0].kind == nkSym) and (n[0].sym.magic != mNone): 2153 | genMagic(p, n, r) 2154 | elif n[0].kind == nkSym and sfInfixCall in n[0].sym.flags and 2155 | n.len >= 1: 2156 | genInfixCall(p, n, r) 2157 | else: 2158 | genCall(p, n, r) 2159 | of nkClosure: gen(p, n[0], r) 2160 | of nkCurly: genSetConstr(p, n, r) 2161 | of nkBracket: genArrayConstr(p, n, r) 2162 | of nkPar, nkTupleConstr: genTupleConstr(p, n, r) 2163 | of nkObjConstr: genObjConstr(p, n, r) 2164 | of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r) 2165 | of nkAddr, nkHiddenAddr: 2166 | genAddr(p, n, r) 2167 | of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r) 2168 | of nkBracketExpr: genArrayAccess(p, n, r) 2169 | of nkDotExpr: genFieldAccess(p, n, r) 2170 | of nkCheckedFieldExpr: genCheckedFieldOp(p, n, nil, r) 2171 | of nkObjDownConv: gen(p, n[0], r) 2172 | of nkObjUpConv: upConv(p, n, r) 2173 | of nkCast: genCast(p, n, r) 2174 | of nkChckRangeF: genRangeChck(p, n, r, "chckRangeF") 2175 | of nkChckRange64: genRangeChck(p, n, r, "chckRange64") 2176 | of nkChckRange: genRangeChck(p, n, r, "chckRange") 2177 | of nkStringToCString: convStrToCStr(p, n, r) 2178 | of nkCStringToString: convCStrToStr(p, n, r) 2179 | of nkEmpty: discard 2180 | of nkLambdaKinds: 2181 | let s = n[namePos].sym 2182 | discard mangleName(p.module, s) 2183 | r.res = s.loc.r 2184 | if lfNoDecl in s.loc.flags or s.magic != mNone: discard 2185 | elif not p.g.generatedSyms.containsOrIncl(s.id): 2186 | p.locals.add(genProc(p, s)) 2187 | of nkType: r.res = genTypeInfo(p, n.typ) 2188 | of nkStmtList, nkStmtListExpr: 2189 | # this shows the distinction is nice for backends and should be kept 2190 | # in the frontend 2191 | let isExpr = not isEmptyType(n.typ) 2192 | for i in 0..