From 19f7c8ad8b4b46a2a7630b9eecd4e88004675e68 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 13 Aug 2023 20:05:31 +0200 Subject: [PATCH] =?UTF-8?q?Updates=20to=20MetaModelica.j=C3=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Project.toml | 1 + src/MetaModelica.jl | 2 +- src/dangerous.jl | 126 ++++++++++++++++++++++++++++++++++++++++--- src/matchcontinue.jl | 4 +- src/metaRuntime.jl | 60 +++++++++++++++------ src/union.jl | 32 ++++++++--- src/utilityMacros.jl | 26 ++++++--- 7 files changed, 208 insertions(+), 43 deletions(-) diff --git a/Project.toml b/Project.toml index 3e7e167..cb62e2f 100644 --- a/Project.toml +++ b/Project.toml @@ -6,6 +6,7 @@ version = "0.0.2" [deps] DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" ExportAll = "ad2082ca-a69e-11e9-38fa-e96309a31fe4" +FastClosures = "9aa1b823-49e4-5ca5-8b0f-3971ec8bab6a" ImmutableList = "4a558cac-c1ed-11e9-20da-3584bcd8709a" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46" diff --git a/src/MetaModelica.jl b/src/MetaModelica.jl index e65feb9..6857a2e 100644 --- a/src/MetaModelica.jl +++ b/src/MetaModelica.jl @@ -26,7 +26,7 @@ export @match, @matchcontinue, MatchFailure, ModelicaReal, ModelicaInteger export @Uniontype, @Record, @UniontypeDecl, @ExtendedFunction, @ExtendedAnonFunction export List, list, Nil, nil, Cons, cons, =>, Option, SOME, NONE, SourceInfo, SOURCEINFO export @do_threaded_for, <|, @shouldFail, sourceInfo, _cons, @importDBG -export @assign +export @assign, @Mutable_Uniontype, @closure include("exportmetaRuntime.jl") include("dangerous.jl") diff --git a/src/dangerous.jl b/src/dangerous.jl index 6064089..41e1a5f 100644 --- a/src/dangerous.jl +++ b/src/dangerous.jl @@ -26,7 +26,8 @@ end access an uninitialized elements may cause segmentation faults if you're lucky, and pretty much anything else if you're not. Do not use unless you will immediately fill the whole array with data. The dummy variable is used to fix -the type of the array. """ +the type of the array. +""" function arrayCreateNoInit(size::ModelicaInteger, dummy::A)::Array{A} where {A} local arr::Array{A} = fill(dummy, size) arr @@ -43,23 +44,134 @@ function listReverseInPlace(inList::List{T})::List{T} where {T} MetaModelica.listReverse(inList) end -""" O(1). A destructive operation changing the \"first\" part of a cons-cell. """ -function listSetFirst(inConsCell::List{A}, inNewContent::A) where {A} #= A non-empty list =# - @error "Not listSetFirst defined in the runtime" + +function listReverseInPlace2(inList::Nil) + return inList#MetaModelica.listReverse(inList) +end + +""" + Unsafe implementation of list reverse in place. + Instead of creating new cons cells we swap pointers... +""" +function listReverseInPlace2(lst::Cons{T}) where {T} + local prev = nil + #= Declare an unsafe pointer to the list =# + local oldCdrPtr::Ptr{List{T}} + GC.@preserve while (!(lst isa Nil)) + println("prev at the iteration:") + println(prev) + println("lst at the iteration:") + println(lst) + println("before oldCdr = $(lst.tail)") + oldCdr = deepcopy(lst.tail) + println("Before listSetRest($lst, $prev)") + listSetRest(lst, prev) + println("Before prev = $lst") + prev = lst + println("Before lst = $(oldCdr) //oldCdr") + lst = oldCdr + end + println("After loop") + return prev +end + + +""" +O(1). A destructive operation changing the \"first\" part of a cons-cell. +TODO: Not implemented +""" +function listSetFirst(inConsCell::Cons{A}, inNewContent::A) where {A} #= A non-empty list =# + firstPtr::Ptr{A} = unsafe_getListAsPtr(inConsCell) + #local newHead = Cons{T}(inNewContent, inConsCell.tail) + # unsafe_store!(firstPtr, inNewContent) end """ O(1). A destructive operation changing the rest part of a cons-cell """ #= NOTE: Make sure you do NOT create cycles as infinite lists are not handled well in the compiler. =# -function listSetRest(inConsCell::List{A}, inNewRest::List{A}) where {A} #= A non-empty list =# - @error "Not listSetRest defined in the runtime" +function listSetRest(inConsCell::Cons{A}, inNewRest::Cons{A}) where {A} #= A non-empty list =# + newTailPtr::Ptr{Cons{A}} = unsafe_getListAsPtr(inNewRest) + inConsCellTailPtr::Ptr{Cons{A}} = unsafe_getListTailAsPtr(inConsCell) + inConsCellTailPtr2::Ptr{Cons{A}} = unsafe_getListAsPtr(inConsCell) + GC.@preserve(unsafe_store!(inConsCellTailPtr, unsafe_load(newTailPtr))) + return inConsCell end +""" + We create one cons cell when the tail we are setting is a nil... +""" +function listSetRest(inConsCell::Cons{A}, inNewRest::Nil) where {A} #= A non-empty list =# + local lstPtr::Ptr{Cons{A}} = unsafe_getListAsPtr(inConsCell) + local val = inConsCell.head + GC.@preserve unsafe_store!(lstPtr, Cons{A}(inConsCell.head, inNewRest)) + return inConsCell +end + + """ O(n) """ function listArrayLiteral(lst::List{A})::Array{A} where {A} local arr::Array{A} = listArray(lst) arr end -@exportAll() +""" +``` +listGetFirstAsPtr(lst::Cons{T})::Ptr{T} +``` + +Dangerous function. +Gets the first element of the list as a pointer of type T. +Unless it is nil then we get a NULL pointer +""" +function unsafe_getListHeadAsPtr(lst::Cons{T}) where{T} + convert(Ptr{T}, unsafe_pointer_from_objref(lst.head)) +end + +""" +``` listGetFirstAsPtr(nil)::Ptr{Nothing}``` +Returns a null pointer +""" +function unsafe_getListHeadAsPtr(lst::Nil) + unsafe_pointer_from_objref(nil) +end + +""" + Fetches the pointer to the tail of the list +``` +unsafe_listGetTailAsPtr{lst::List{T}}::Ptr{Cons{T}} +``` +""" +function unsafe_getListTailAsPtr(lst::List{T}) where {T} + if lst.tail === nil + return unsafe_pointer_from_objref(nil) + else + convert(Ptr{Cons{T}}, unsafe_pointer_from_objref(lst.tail)) + end +end + +""" +Unsafley get a pointer to a list. +""" +function unsafe_getListAsPtr(lst::List{T}) where {T} + if lst === nil + ptrToNil::Ptr{Nil{Any}} = unsafe_pointer_from_objref(nil) + return ptrToNil + else + convert(Ptr{Cons{T}}, unsafe_pointer_from_objref(lst)) + end +end + + + + +""" + Unsafe function to get pointers from immutable struct. + Use with !care! +""" +function unsafe_pointer_from_objref(@nospecialize(x)) + ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), x) +end + + +ExportAll.@exportAll() end #=End dangerous =# diff --git a/src/matchcontinue.jl b/src/matchcontinue.jl index 6305f90..7a7a268 100644 --- a/src/matchcontinue.jl +++ b/src/matchcontinue.jl @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. - The code is based on https://github.com/RelationalAI-oss/Rematch.jl with - changes to allow keyword argument matching on structs along with + The code is originally based on https://github.com/RelationalAI-oss/Rematch.jl with + changes to allow keyword argument matching on structs along with matching on the immutable list construct accompanying MetaModelica + some other improvements and bug fixes. It also provides @matchcontinue macro (try the next case when any exception is thrown). """ diff --git a/src/metaRuntime.jl b/src/metaRuntime.jl index 2f122b7..a19ccf5 100644 --- a/src/metaRuntime.jl +++ b/src/metaRuntime.jl @@ -369,15 +369,17 @@ end Example: stringDelimitList({\"x\",\"y\",\"z\"}, \", \") => \"x, y, z\" """ function stringDelimitList(strs::List{String}, delimiter::String)::String - local str::String = "" - for n in strs - if isempty(str) - str = n + buffer = IOBuffer() + for (i,n) in enumerate(strs) + if i == 1 + print(buffer, n) else - str = str + delimiter + n + print(buffer, delimiter) + print(buffer, n) + #str = str + delimiter + n end end - str + return String(take!(buffer))#str end function stringDelimitList(strs::List, delimiter::String)::String @@ -516,7 +518,7 @@ function arrayCreate(size::ModelicaInteger, initialValue::A)::Array{A} where {A} end """ O(N) """ -function arrayList(arr::Array{T})::List{T} where {T} +function arrayList(arr::Array{T}) where {T} local lst::List{T} = nil for i = length(arr):-1:1 lst = Cons{T}(arr[i], lst) @@ -525,12 +527,16 @@ function arrayList(arr::Array{T})::List{T} where {T} end """ O(n) """ -function listArray(lst::Cons{T})::Array{T} where {T} - local arr::Vector{T} = T[] - for i in lst - push!(arr, i) +function listArray(lst::Cons{T}) where {T} + local N = length(lst) + local arr::Vector{T} = Vector{T}(undef, N) + i = 1 + while lst !== nil + arr[i] = lst.head + i += 1 + lst = lst.tail end - arr + return arr end """ O(1) """ @@ -538,6 +544,26 @@ function listArray(lst::Nil) [] end +""" +O(n) + +Same as listArray but with a dummy argument to specify the type. +""" +function listArray(lst::Cons{T}, ty) where {T} + local arr = Vector{ty}(undef, length(lst)) + for i in lst + arr[i] + end + return arr +end + +""" +Same as listArray but with a dummy argument to specify the type. +""" +function listArray(lst::Nil, ty) + ty[] +end + """ O(1) """ function arrayUpdate(arr::Array{A}, index::ModelicaInteger, newValue::B)::Array{A} where {A,B} @@ -556,9 +582,8 @@ end Note that this operation is *not* destructive, i.e. a new array is created. """ function arrayAppend(arr1::Array{A}, arr2::Array{A})::Array{A} where {A} local arr::Array{A} - - #= Defined in the runtime =# - arr + @error "Defined in the runtime" + fail() end """ Returns the string representation of any value. @@ -603,7 +628,7 @@ end This is a global mutable value and should be used sparingly. You are recommended not to use "missing" the runtime system treats this values as uninitialized and fail getGlobalRoot later on. """ -global globalRoots = Array{Any,1}(missing, 1024) +const global globalRoots::Vector{Any} = Vector{Any}(missing, 1024) function setGlobalRoot(index::ModelicaInteger, value::T) where {T} if index > 1023 || index < 0 @@ -733,7 +758,8 @@ function referenceDebugString(functionSymbol::A)::String where {A} name end -""" TODO: I am far from sure that this will fly.. in Julia. The code generated from the transpiler is correct however""" +""" TODO: I am far from sure that this will fly.. in Julia. +The code generated from the transpiler is correct however""" function isPresent(ident::T)::Bool where {T} local b::Bool b = true diff --git a/src/union.jl b/src/union.jl index 9648707..37c02e5 100644 --- a/src/union.jl +++ b/src/union.jl @@ -74,15 +74,23 @@ end function replaceLineNum(a::Any, lines::LineNumberNode) end -function makeUniontypes(name, records, lineNode::LineNumberNode) +function makeUniontypes(name, records, lineNode::LineNumberNode; mutable = false) recordsArray1 = Array.(records) recordsArray2 = recordsArray1[1] constructedRecords = [] - for r in recordsArray2 + for r in recordsArray2 structName = r[1] - recordNode = quote - struct $(structName) <: $name - $(r[2]) + recordNode = if ! mutable + quote + struct $(structName) <: $name + $(r[2]) + end + end + else + quote + mutable struct $(structName) <: $name + $(r[2]) + end end end replaceLineNum(recordNode, isa(r[3], Nothing) ? lineNode : r[3]) @@ -99,13 +107,21 @@ function makeUniontypes(name, records, lineNode::LineNumberNode) return res end -#= Creates a uniontype consisting of 0...N records =# +""" Creates a uniontype consisting of 0...N records """ macro Uniontype(name, records...) recordCollection = [makeRecord(r) for r in records] esc(makeUniontypes(name, recordCollection, __source__)) end -#= Creates a record belonging to a Uniontype =# +""" + Creates a mutable uniontype constisting of 0...N records +""" +macro Mutable_Uniontype(name, records...) + recordCollection = [makeRecord(r) for r in records] + esc(makeUniontypes(name, recordCollection, __source__; mutable = true)) +end + +""" Creates a record belonging to a Uniontype """ macro Record(name, fields...) makeTuple(name, fields) end @@ -117,6 +133,6 @@ macro UniontypeDecl(uDecl) end) end -export @Uniontype, @Record, @UniontypeDecl +export @Uniontype, @Record, @UniontypeDecl, @Mutable_Uniontype end diff --git a/src/utilityMacros.jl b/src/utilityMacros.jl index fa557b8..f259235 100644 --- a/src/utilityMacros.jl +++ b/src/utilityMacros.jl @@ -1,23 +1,25 @@ import Setfield +import FastClosures """ Helper function for the assignmacro, see @assign We have one case where we assign a immutable structure to an immutable structure or something to a primitive variable. If it is not a primitive we assign to a subcomponent of that structure. We then clone the structure with that particular field changed. """ function assignFunc(expr) - res = if @capture(expr, lhs_._ = rhs_) - if !isprimitivetype(typeof(lhs)) - Setfield.setmacro(identity, expr, overwrite=true) + res = + if @capture(expr, lhs_._ = rhs_) + if !isprimitivetype(typeof(lhs)) + Setfield.setmacro(identity, expr, overwrite=true) + else + quote + $(esc(expr)) + end + end else quote $(esc(expr)) end end - else - quote - $(esc(expr)) - end - end return res end @@ -33,3 +35,11 @@ E.g.: macro assign(expr) assignFunc(expr) end + +""" + Wraps the @closure macro of FastClosures. +See the FastClosure package for more information. +""" +macro closure(expr) + esc(FastClosures.wrap_closure(__module__, expr)) +end