• Skip to main content
  • Skip to search
  • Skip to footer
Cadence Home
  • This search text may be transcribed, used, stored, or accessed by our third-party service providers per our Cookie Policy and Privacy Policy.

  1. Community Forums
  2. Custom IC SKILL
  3. How can a concatenated string be transformed into a variable...

Stats

  • Replies 8
  • Subscribers 151
  • Views 92
  • Members are here 0

How can a concatenated string be transformed into a variable name?

SZ20260803361
SZ20260803361 1 day ago

I want to set a variable name that can change over time. For example:
item=""
lists=setof(i parseString(getCurrentTime() "") i!) =" "&&i! =":")
foreach(elem lists

item=strcat(item elem)
)
I received the item with the code "Aug201706212026".
I want to replace the "item" in the variable name "a_item" with "Aug201706212026", so that the final variable name becomes "a_Aug201706212026". How can I do this? I tried using `setq(stringToSymbol(strcat("a_" item)) 5)`, but this caused an error. How should I proceed?

  • Sign in to reply
  • Cancel

Top Replies

  • Andrew Beckett
    Andrew Beckett 21 hours ago +1
    This is almost certainly not what you want to do, because creating different variable names on the fly usually means you should be creating a different data structure - e.g. a hash table indexed by the…
Parents
  • Andrew Beckett
    Andrew Beckett 21 hours ago

    This is almost certainly not what you want to do, because creating different variable names on the fly usually means you should be creating a different data structure - e.g. a hash table indexed by the date rather than a unique variable name. However, if you really want to do this (the wrong way in my opinion), then taking your code:

    item=""
    lists=setof(i parseString(getCurrentTime() "") i!=" " && i!=":")
    foreach(elem lists
        item=strcat(item elem)
    )

    (I assume you used some kind of optical character recognition because it had some spurious parentheses) then you could do:

    set(concat("a_" item) 5)

    you can retrieve it with:

    symeval(concat("a_" item))

    A cleaner (more efficient in terms of creating garbage that needs collecting) way of building the variable name would be to do:

    cleanup=pcreCompile("[ :]")
    varName=concat("a_" pcreReplace(cleanup getCurrentTime() "" 0))

    However, if you creating a number of these, I'd use a hash table (using makeTable) as I mentioned before.

    Regards,

    Andrew

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • Cancel
  • SZ20260803361
    SZ20260803361 4 hours ago in reply to Andrew Beckett

    Source code is shown below

    load_form.il:

    (let (load_en reload_en)

    procedure(load_form()
    cond(
    (dock_W
    load_en = nil
    reload_en = t
    )
    (t
    load_en = t
    reload_en = nil
    )
    )

    layoutForm_pte~>loadpte_button~>enabled = load_en
    layoutForm_pte~>reloadpte_button~>enabled = reload_en

    hiDisplayForm('layoutForm_pte)
    )

    )




    load_procedure.il:

    (let (a b ns)

    procedure(loadpte(a)
    load("test_layout_demand.il")
    hiFormClose(a)
    layoutForm_pte~>loadpte_button~>enabled = nil
    layoutForm_pte~>reloadpte_button~>enabled = t
    )

    procedure(reloadpte(a)
    b=hiDockWindow(
    ?window dock_W
    )

    if(b==t then
    hiFormClose(a)
    else
    dock_W=hiCreateDockWindow(
    ?appType "layout"
    ?widgetType "form"
    ?form appForm
    )

    ns=hiGetWindowList('session)

    foreach( n ns
    hiDockWindow(
    ?window dock_W
    ?session n
    )
    )
    )
    )
    )





    gui_contain.il:

    (let (load_en reload_en loadpte_button reloadpte_button loadpte reloadpte boxLayout_ver )

    dock_W = nil
    load_en = t
    reload_en = nil


    loadpte_button = hiCreateButton(
    ?name 'loadpte_button
    ?buttonText "Press this button for initial loading:"
    ?callback "loadpte(layoutForm_pte)"
    ?enabled load_en
    )


    reloadpte_button = hiCreateButton(
    ?name 'reloadpte_button
    ?buttonText "Click to Reload: "
    ?callback "reloadpte(layoutForm_pte)"
    ?enabled reload_en
    )

    boxLayout_ver = hiCreateVerticalBoxLayout(
    'boxLayout_ver
    ?items list(loadpte_button reloadpte_button)
    )

    layoutForm_pte = hiCreateLayoutForm(
    'layoutForm_pte "LOAD PTE" boxLayout_ver
    )

    )

    test_layout_demand.il:

    (let (addMosW_string addMosW_button gridLayout boxLayout loadpte_button reloadpte_button boxLayout_ver ns)


    addMosW_string = hiCreateStringField(
    ?name 'addMosW_string
    ?prompt "M1_width: "
    ?defValue "1.5"
    )


    addMosW_button = hiCreateButton(
    ?name 'addMosW_button
    ?buttonText "apply"
    ?callback "(AddMosM1)"
    )

    gridLayout = hiCreateGridLayout( 'gridLayout
    ?items (list
    list( addMosW_string 'row 0 'col 0 )
    list( addMosW_button 'row 0 'col 1 )
    list( 'col_stretch 0 1)
    list( 'col_stretch 1 0)
    )
    )

    boxLayout = hiCreateHorizontalBoxLayout(
    'boxLayout
    ?items list(gridLayout)
    )


    appForm = hiCreateLayoutForm(
    'appForm "LAYOUT_PTE" boxLayout
    )


    procedure(AddMosM1()
    let((cv objs metal1 temp temp1 shapes x y ux uy xy row column transform M1_width)
    M1_width = evalstring(appForm~>addMosW_string~>value)
    cv = geGetEditCellView()
    objs = geGetSelSet()
    foreach(obj objs
    transform = nil
    if(obj~>objType == "inst" then transform = list(obj~>transform));end if
    if(obj~>objType == "mosaic" then
    x = xCoord(obj~>xy)
    y = yCoord(obj~>xy)
    ux = obj~>uX
    uy = obj~>uY
    row = obj~>rows
    column = obj~>columns
    for(a 0 row-1
    for(b 0 column-1
    case(car(obj~>tileArray)
    ("R0" || "MXR90" xy = list(x+a*ux : y+b*uy))
    ("R90" || "MY" xy = list(x-a*ux : y+b*uy))
    ("R0" || "MXR90" xy = list(x+a*ux : y-b*uy))
    ("R0" || "MXR90" xy = list(x-a*ux : y-b*uy))
    );end case
    transform = append1(transform list(car(xy) car(obj~>tileArray) 1.0))
    );end for
    );enf for
    );end if
    shapes = obj~>master~>shapes
    foreach(shape shapes
    if(car(shape~>lpp) == "M1" && cadr(shape~>lpp) == "drawing"
    then
    foreach(trans transform
    metal1 = dbCopyFig(shape cv trans)
    temp = leConvertPolygonToPath(metal1)
    temp~>width = M1_width
    );foreach
    );if
    );foreach
    );foreach
    );let
    );proc


    dock_W=hiCreateDockWindow(
    ?appType "layout"
    ?widgetType "form"
    ?form appForm
    )

    ns=hiGetWindowList('session)

    foreach( n ns
    hiDockWindow(
    ?window dock_W
    ?session n
    )
    )
    )

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • Andrew Beckett
    Andrew Beckett 1 hour ago in reply to SZ20260803361

    I reformatted the code to make it easier to understand (without the indentation it's nigh on impossible):

    load_form.il

    (let (load_en reload_en)
    
      procedure(load_form()
        cond(
          (dock_W
            load_en = nil
            reload_en = t
          )
          (t
            load_en = t
            reload_en = nil
          )
        )
    
        layoutForm_pte~>loadpte_button~>enabled = load_en
        layoutForm_pte~>reloadpte_button~>enabled = reload_en
    
        hiDisplayForm('layoutForm_pte)
      )
    
    )

    load_procedure.il

    (let (a b ns)
    
      procedure(loadpte(a)
        load("test_layout_demand.il")
        hiFormClose(a)
        layoutForm_pte~>loadpte_button~>enabled = nil
        layoutForm_pte~>reloadpte_button~>enabled = t
      )
    
      procedure(reloadpte(a)
        b=hiDockWindow(
          ?window dock_W
        )
    
        if(b==t then
          hiFormClose(a)
        else
          dock_W=hiCreateDockWindow(
    	?appType "layout"
    	?widgetType "form"
    	?form appForm
          )
    
          ns=hiGetWindowList('session)
    
          foreach( n ns
    	hiDockWindow(
    	  ?window dock_W
    	  ?session n
    	)
          )
        )
      )
    )
    

    gui_contain.il

    (let (load_en reload_en loadpte_button reloadpte_button loadpte reloadpte boxLayout_ver )
    
      dock_W = nil
      load_en = t
      reload_en = nil
    
    
      loadpte_button = hiCreateButton(
        ?name 'loadpte_button
        ?buttonText "Press this button for initial loading:"
        ?callback "loadpte(layoutForm_pte)"
        ?enabled load_en
      )
    
    
      reloadpte_button = hiCreateButton(
        ?name 'reloadpte_button
        ?buttonText "Click to Reload: "
        ?callback "reloadpte(layoutForm_pte)"
        ?enabled reload_en
      )
    
      boxLayout_ver = hiCreateVerticalBoxLayout(
        'boxLayout_ver
        ?items list(loadpte_button reloadpte_button)
      )
    
      layoutForm_pte = hiCreateLayoutForm(
        'layoutForm_pte "LOAD PTE" boxLayout_ver
      )
    
    )

    test_layout_demand.il

    (let (addMosW_string addMosW_button gridLayout boxLayout loadpte_button reloadpte_button boxLayout_ver ns)
    
    
      addMosW_string = hiCreateStringField(
        ?name 'addMosW_string
        ?prompt "M1_width: "
        ?defValue "1.5"
      )
    
    
      addMosW_button = hiCreateButton(
        ?name 'addMosW_button
        ?buttonText "apply"
        ?callback "(AddMosM1)"
      )
    
      gridLayout = hiCreateGridLayout( 'gridLayout
        ?items (list
          list( addMosW_string 'row 0 'col 0 )
          list( addMosW_button 'row 0 'col 1 )
          list( 'col_stretch 0 1)
          list( 'col_stretch 1 0)
        )
      )
    
      boxLayout = hiCreateHorizontalBoxLayout(
        'boxLayout
        ?items list(gridLayout)
      )
    
    
      appForm = hiCreateLayoutForm(
        'appForm "LAYOUT_PTE" boxLayout
      )
    
    
      procedure(AddMosM1()
        let((cv objs metal1 temp temp1 shapes x y ux uy xy row column transform M1_width)
          M1_width = evalstring(appForm~>addMosW_string~>value)
          cv = geGetEditCellView()
          objs = geGetSelSet()
          foreach(obj objs
            transform = nil
            if(obj~>objType == "inst" then transform = list(obj~>transform));end if
            if(obj~>objType == "mosaic" then
              x = xCoord(obj~>xy)
              y = yCoord(obj~>xy)
              ux = obj~>uX
              uy = obj~>uY
              row = obj~>rows
              column = obj~>columns
              for(a 0 row-1
                for(b 0 column-1
                  case(car(obj~>tileArray)
                    ("R0" || "MXR90" xy = list(x+a*ux : y+b*uy))
                    ("R90" || "MY" xy = list(x-a*ux : y+b*uy))
                    ("R0" || "MXR90" xy = list(x+a*ux : y-b*uy))
                    ("R0" || "MXR90" xy = list(x-a*ux : y-b*uy))
                  );end case
                  transform = append1(transform list(car(xy) car(obj~>tileArray) 1.0))
                );end for
              );enf for
            );end if
            shapes = obj~>master~>shapes
            foreach(shape shapes
              ; Andrew changed M1 to Metal1 for testing
              if(car(shape~>lpp) == "Metal1" && cadr(shape~>lpp) == "drawing"
                then
                  foreach(trans transform
                    metal1 = dbCopyFig(shape cv trans)
                    temp = leConvertPolygonToPath(metal1)
                    temp~>width = M1_width
                  );foreach
              );if
            );foreach
          );foreach
        );let
      );proc
    
    
      dock_W=hiCreateDockWindow(
        ?appType "layout"
        ?widgetType "form"
        ?form appForm
      )
    
      ns=hiGetWindowList('session)
    
      foreach( n ns
        hiDockWindow(
          ?window dock_W
          ?session n
        )
      )
    )

    My first observations are:

    1. You are using SKILL (not SKILL++) and have procedures defined within lets. These are not lexically scoped, and hence any variables they reference in a surrounding let are actually not local - they are global. This makes the code confusing and almost certainly is not doing what you think it's doing. SKILL++ mode (having the files with a .ils suffix) does introduce lexical scoping but then the procedures would be local (and you'd need to use globalProc to make them visible from outside) - but then some of the form variables wouldn't work.
    2. You will need a separate dock window for each session window, but you don't need to store that in a global variable
    3. You will need separate global variables for each form. Each (independent) form needs to have a unique global variable to represent it - you can't just re-use the same variable after constructing it (the reason is that the name needs to be known so that SKILL expressions can be generated as you interact with the form - you'll see these in the replay). You can use gensym() to create a unique symbol each time

    Anyway, I'll restructure your code a little to tidy things up and show how this can be done. This will take me a little while to clean up what you've done... I'll be back soon!

    Andrew

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • SZ20260803361
    SZ20260803361 1 hour ago in reply to Andrew Beckett

    Thank you very much. I will take some time to go through this and get my head around it.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
Reply
  • SZ20260803361
    SZ20260803361 1 hour ago in reply to Andrew Beckett

    Thank you very much. I will take some time to go through this and get my head around it.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
Children
  • Andrew Beckett
    Andrew Beckett 55 minutes ago in reply to SZ20260803361

    OK, here's the new code. This copes with multiple windows and (slightly) alters the initial form so that you can force the dock windows to be recreated (you may or may not want this). I've added AB comments in various places to try to explain the key differences (but there may be subtle differences here and there that I've glossed over; hopefully they are obvious though).

    BTW, you should really use a prefix for your functions to avoid clashing with others - AddMosM1 is not that unique... (the new functions I added also didn't have prefixes - I left that as an exercise for you).

    load_form.il

    ; AB: removed the local vars which weren't local
    
    procedure(load_form()
    
      layoutForm_pte~>loadpte_button~>enabled = t
      layoutForm_pte~>reloadpte_button~>enabled = t
    
      hiDisplayForm('layoutForm_pte)
    )
    

    load_procedure.il

    ; AB: completely rewrote. Don't really need a reload, but 
    ; in case you need it, added an optional argument to force
    ; recreation of the dock window and form
    procedure(loadpte(form @optional force_recreate)
      let((sess_win_list dock_W)
        hiFormClose(form)
        sess_win_list=hiGetWindowList('session)
        foreach( sess_win sess_win_list
          dock_W=sess_win~>myDockWin
          if(dock_W && !force_recreate then
            hiDockWindow(
              ?window dock_W
              ?session sess_win
            )
          else
            ; need to ensure that if there was an existing dock window
            ; we close it otherwise you'll end up with more than one!
            when(dock_W
              hiCloseWindow(dock_W)
            )
            CreateDockWinForm(sess_win)
          )
        )
        t
      )
    )

    gui_contain.il. (I didn't put this inside a procedure because there's only one top level form)

    ; AB: removed unused variabls and stopped setting dock_W here
    (let (load_en reload_en loadpte_button reloadpte_button boxLayout_ver )
    
      load_en = t
      reload_en = nil
    
      loadpte_button = hiCreateButton(
        ?name 'loadpte_button
        ?buttonText "Press this button for initial loading:"
        ?callback "loadpte(hiGetCurrentForm())"
        ?enabled load_en
      )
    
    
      reloadpte_button = hiCreateButton(
        ?name 'reloadpte_button
        ?buttonText "Click to Reload: "
        ?callback "loadpte(hiGetCurrentForm() t)"
        ?enabled reload_en
      )
    
      boxLayout_ver = hiCreateVerticalBoxLayout(
        'boxLayout_ver
        ?items list(loadpte_button reloadpte_button)
      )
    
      layoutForm_pte = hiCreateLayoutForm(
        'layoutForm_pte "LOAD PTE" boxLayout_ver
      )
    
    )

    test_layout_demand.il

    ; AB: put the form and dock creation code within a function
    ; also removed unused local vars
    procedure(CreateDockWinForm(sess_win)
      let((addMosW_string addMosW_button gridLayout dock_W theForm boxLayout formName)
    
        formName=gensym('appForm)
    
        addMosW_string = hiCreateStringField(
          ?name 'addMosW_string
          ?prompt "M1_width: "
          ?defValue "1.5"
        )
    
        addMosW_button = hiCreateButton(
          ?name 'addMosW_button
          ?buttonText "apply"
          ?callback lsprintf("AddMosM1(%s)" formName)
        )
    
        gridLayout = hiCreateGridLayout( 'gridLayout
          ?items (list
            list( addMosW_string 'row 0 'col 0 )
            list( addMosW_button 'row 0 'col 1 )
            list( 'col_stretch 0 1)
            list( 'col_stretch 1 0)
          )
        )
    
        boxLayout = hiCreateHorizontalBoxLayout(
          'boxLayout
          ?items list(gridLayout)
        )
    
        theForm = hiCreateLayoutForm(
          formName "LAYOUT_PTE" boxLayout
        )
    
        dock_W=hiCreateDockWindow(
          ?appType "layout"
          ?widgetType "form"
          ?form theForm
        )
    
        hiDockWindow(
          ?window dock_W
          ?session sess_win
        )
        ; AB: record it as a property on the window in case we need it later?
        sess_win->myDockWin=dock_W
    
        ; AB: return the dock window
        dock_W
    
      )
    )
    
    ; AB: pass the form to the callback
    procedure(AddMosM1(form)
      let((cv objs metal1 temp shapes x y ux uy xy row column transform M1_width)
        ; don't use evalstring. Imagine what happens if somebody types in exit() in the field!
        ;M1_width = evalstring(appForm~>addMosW_string~>value)
        M1_width = atof(form~>addMosW_string~>value)
        cv = geGetEditCellView()
        objs = geGetSelSet()
        foreach(obj objs
          transform = nil
          if(obj~>objType == "inst" then transform = list(obj~>transform));end if
          if(obj~>objType == "mosaic" then
            x = xCoord(obj~>xy)
            y = yCoord(obj~>xy)
            ux = obj~>uX
            uy = obj~>uY
            row = obj~>rows
            column = obj~>columns
            for(a 0 row-1
              for(b 0 column-1
                ; AB: you can't use or within a case and you have duplicate values
                ; I changed the || to correct syntax (a list of candidates), but you'll need to fix the logic
                ; if "R0" or "MXR90" is found, only the first row will be chosen
                case(car(obj~>tileArray)
                  (("R0" "MXR90") xy = list(x+a*ux : y+b*uy))
                  (("R90" "MY")   xy = list(x-a*ux : y+b*uy))
                  (("R0" "MXR90") xy = list(x+a*ux : y-b*uy))
                  (("R0" "MXR90") xy = list(x-a*ux : y-b*uy))
                );end case
                transform = append1(transform list(car(xy) car(obj~>tileArray) 1.0))
              );end for
            );enf for
          );end if
          shapes = obj~>master~>shapes
          foreach(shape shapes
            ; Andrew changed M1 to Metal1 for testing
            if(car(shape~>lpp) == "Metal1" && cadr(shape~>lpp) == "drawing"
              then
                foreach(trans transform
                  metal1 = dbCopyFig(shape cv trans)
                  temp = leConvertPolygonToPath(metal1)
                  temp~>width = M1_width
                );foreach
            );if
          );foreach
        );foreach
      );let
    );proc

    Good luck!

    Andrew

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel
  • SZ20260803361
    SZ20260803361 35 minutes ago in reply to Andrew Beckett

    Thank you so much for the revised code and the explanatory comments. I will go through it carefully to understand the changes.

    I totally agree with your suggestion about adding custom prefixes to my functions to prevent naming conflicts. 

    I will test the logic for recreating dock windows. I may come back with further questions if I run into any issues.

    Thanks again for your great help!

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • Cancel

Community Guidelines

The Cadence Design Communities support Cadence users and technologists interacting to exchange ideas, news, technical information, and best practices to solve problems and get the most from Cadence technology. The community is open to everyone, and to provide the most value, we require participants to follow our Community Guidelines that facilitate a quality exchange of ideas and information. By accessing, contributing, using or downloading any materials from the site, you agree to be bound by the full Community Guidelines.

© 2026 Cadence Design Systems, Inc. All Rights Reserved.

  • Terms of Use
  • Privacy
  • Cookie Policy
  • US Trademarks
  • Do Not Sell or Share My Personal Information