[ Skill ] Cadence Skill 語言入門


https://www.cnblogs.com/yeungchie/

寫個大筆記,低速更新中 ...

Cadence Skill

Cadence 提供二次開發的 SKILL 語言,它是一種基於通用人工智能語言— Lisp 的交互式高級編程語言 ①。

SKILL 語言支持一套類似 C 語言的語法,大大降低了初學者學習的難度,同時高水平的編程者可以選擇使用類似 Lisp 語言的全部功能。所以 SKILL 語言既可以用作最簡單的工具語言,也可以作為開發任何應用的、強大的編程語言。

SKILL 可以與底層系統交互,也提供了訪問 Cadence 各個工具的豐富接口。用戶可以通過 Skill 語言來訪問,並且可以開發自己的基於 Cadence 平台的工具。

① LISP 即 List Processing-表處理,是最早和最重要的符號處理編程語言之一,它於1958年由美國的 J. McCarthy 提出,LISP 在人工智能AI方面獲得廣泛應用。

我的環境是 Virtuoso IC618 平台,可能存在某些特性不適用於舊版本。

如何查看官方資料

cdsFinder

  • 用於模糊查找 Skill 函數,查看簡單介紹。
cdsFinder &
# $CDSHOME/tools/bin/cdsFinder

cdnshelp

  • 用於查看更加詳細的內容,軟件的使用手冊。
cdnshelp &
# $CDSHOME/tools/bin/cdnshelp

代碼風格

語法風格

由於 Skill 語言是基於 Lisp ,因此它支持 函數表示法前綴表示法 來書寫代碼。

  • 函數表示法

    func( arg1 arg2 )
    
  • 前綴表示法

    ( func arg1 arg2 )
    

以上的兩種表示法可以同時存在,因此在編寫代碼時候最好注意格式的統一。

推薦統一使用 函數表示法,並需要注意函數名和括號之間不能有空格。

錯誤寫法 : func ( arg1 arg2 )

func 后面多了一個空格,這會將 arg1 作為函數名

命名風格

其次函數和變量的命名風格,Skill 中一般是使用 駝峰命名法 ( Camel-Case ),函數名中的每一個邏輯斷點(單詞)首字母大寫,開頭第一個單詞全部小寫,一般代表 函數/變量 的類別、作者,像我寫的函數和全局變量一般都習慣用 yc 開頭。

這是官方使用的命名方式,也是現在流行的一種命名方式。

當然如何選擇取決於你自己。

下面是一個對比

  • 駝峰命名法

    geGetEditCellView()
    dbOpenCellViewByType(libName cellName viewName nil mode)
    
  • 蛇形命名法(下划線)

    不推薦。

    ge_get_edit_cell_view()
    db_open_cell_view_by_type(lib_name cell_name view_name nil mode)
    

最后再注意命名不能過於簡化,例如 abc。命名的目標是至少讓人能一眼看出來這個變量是做什么用的(一眼不行看兩眼),例如上面的 libNamecellNameviewName

下面介紹語法部分我會使用的一些非常簡化變量命名,因為只是用於演示,命名就會比較隨意了。

基礎語法

hello world

舉例一種 hello world 的寫法。

println( "hello world" )
; "hello world"

注釋

  • 單行注釋
; 這是單行注釋
  • 多行注釋
/*
  這是
  多行注釋
*/

真 / 假

Skill 中 真用 t 來表示,假用 nil 來表示。

其實參與判斷中的值,除了 nil 和空鏈表以外都可以認為是真,例如:

  • 判斷 t

    when( t
      println( "True" )
    )
    ; t 為真,打印 "True"
    
  • 判斷 10

    when( 10
      println( "True" )
    )
    ; 10 為真,打印 "True"
    
  • 判斷 "YEUNGCHIE"

    when( "YEUNGCHIE"
      println( "True" )
    )
    ; "YEUNGCHIE" 為真,打印 "True"
    
  • 判斷 nil

    when( nil
      println( "True" )
    )
    ; nil 為假,不運行 println 語句
    

when 語句中第一個參數為判斷條件,條件為真才會運行。

數據類型

可以用函數 type 查看一個數據的類型標識。

type( 'YEUNGCHIE )          ; symbol
type( "YEUNGCHIE" )         ; string
type( list( "YEUNGCHIE" ))  ; list

println 打印一個數據的內容,同時也能夠從打印結果觀察到數據類型。

println( 'YEUNGCHIE )          ; YEUNGCHIE
println( "YEUNGCHIE" )         ; "YEUNGCHIE"
println( list( "YEUNGCHIE" ))  ; ( YEUNGCHIE )

數據類型標識

官方的教程中一般會用來表明函數需要的輸入數據是什么類型的( 標在前綴 )。也用於限制子函數輸入數據的類型。

前綴 內部命名 數據類型
d dbobject id , Cadence 數據對象
x integer 整數
f flonum 浮點數
n number 整數 或者 浮點數
g general 通用的 , 任何數據類型
l list 鏈表
p port I / O 句柄
t string 字符串
s symbol " 符號 "
S stringSymbol " 符號 " 或者 字符串
u function 函數對象 , 函數名 或者 lambda對象
... ... ...

字符串

  • 定義方式

字符串用雙引號括起來。

"YEUNGCHIE"
; "YEUNGCHIE"
  • 相關函數

    • strcat 字符串連接

      a = "YEUNG"
      b = "CHIE"
      c = strcat( a b )
      println( c )
      ; "YEUNGCHIE"
      
    • strlen 字符串長度

      下面的 c 沿用上面的變量 c,為了演示不寫得過於繁雜、重復。

      println(strlen( c ))
      ; 9
      

數字

數字分為 整數 和 浮點數。

  • 整數

    18
    

    也可以直接編寫成 二進制 ( 0b 前綴 )、八進制 ( 0 前綴 )、十六進制 ( 0x 前綴 ),但默認會輸出成 十進制

    0b10010    ; 18
    024        ; 20
    0xFE       ; 254
    
  • 浮點數

    3.14
    

    浮點數也可以使用 科學計數法 和 單位后綴 來表示

    1e-06    ; 0.000001
    1u       ; 0.000001
    

symbol(符號)

symbol 的寫法是用一個單引號開頭,例如:'a'b'c,這個類型比較抽象,知道有這么個東西就行,重點是需要用到的時候知道如何去使用。

symbol 實際上是一種指針。

每個 symbol 都存在以下幾個組成部分(slot):

  • Print name ( name )
  • Value
  • Function binding
  • Property list

只有 name 是必要的,其他的部分都可以為空,當一個 文本引用 產生時,系統會創建一個 symbol ,且 Value 會設置為 'unbound

這段話不知道怎么講更好,貼一下原文

The system creates a symbol whenever it encounters a text reference to the symbol for the first time. When the system creates a new symbol, the value of the symbol is set to unbound.

意思就是創建一個變量會自動生成與之對應的一個 symbol ,默認值為 'unbound,這個過程是非顯式的。

這樣,如果需要將一個變量設置為 未定義/未綁定 的狀態,則可以將它賦值為 'unbound

舉個例子:

  • 給 arg 賦值為 12,然后打印出來。

    arg = 12
    println( arg )
    ; 終端會打印 12
    
  • 給 arg 再賦值為 'unbound,然后嘗試 print 一下。

    arg = 'unbound
    println( arg )
    ; 終端會提示 *Error* eval: unbound variable - arg
    

當一個變量沒有被定義過的時候,引用它但不賦值系統會報 Error ,但如果只是想判斷某個變量是否被定義了,可以使用函數 boundp 去檢測目標變量的 symbol name。

例如,檢測一下變量 arg 是否被定義:

  • arg 已經被賦值為 'unbound 現在是未定義的狀態。

    boundp( 'arg )
    ; 結果是 nil
    
  • 再給它賦值一個值。

    arg = 1
    boundp( 'arg )
    ; 結果是 t
    

判斷一個函數是否存在的時候也需要利用到 symbol。

例如,判斷一下函數 dbCreateRect 是否存在:

dbCreateRect 是 Virtuoso 自帶函數。

fboundp( 'dbCreateRect )
; 結果是 lambda:dbCreateRect

判斷一下,函數 QWE123 是否存在:

fboundp( 'QWE123 )
; 不存在,結果是 nil

鏈表 list

鏈表其實不是一種數據類型,而是一種數據的存儲結構。

  • 定義方式

    • list

      list( arg1 arg2 list( arg3 arg4 ) ... )

      listA = list( 1 2 )
      ; ( 1 2 )
      
    • '

      '( "value1" sym1 (1 2) ... )

      這種表達方式需要注意,它不適用於變量元素,例如上面 sym1 並不會帶入變量值,而是 symbol 類型 'sym1

      listB = '( 3 4 )
      ; ( 3 4 )
      
    • :

      arg1 : arg2

      僅在只有兩個元素時使用,通常用來表達一個坐標點

      point = 1 : 2
      ; ( 1 2 )
      
  • 相關函數

    • append 連接兩個 list

      listC = append( listA listB )
      println( listC )
      ; ( 1 2 3 4 )
      
    • append1 往末尾追加元素

      listD = append1( listC 5 )
      println( listD )
      ; ( 1 2 3 4 5 )
      
    • cons 往開頭追加

      listE = cons( 0 listD )
      println( listE )
      ; ( 0 1 2 3 4 5 )
      
    • reverse 翻轉一個 list

      listF = reverse( listE )
      println( listF )
      ; ( 5 4 3 2 1 0 )
      
    • length 獲取一個 list 元素個數

      length( listF )
      ; 6
      
    • car 提取第一個元素

      car( listF )
      ; 5
      
    • cdr 提取除了第一個元素之后的 list

      cdr( listF )
      ; ( 4 3 2 1 0 )
      
    • cadr 及其更多組合

      cadr( listF ) 其實就是 car(cdr( listF )) 的簡寫

      cadr( listF )  ; 4
      caddr( listF ) ; 3
      
    • nth 根據索引提取

      nth( 0 listF )               ; 5
      nth( 1 listF )               ; 4
      nth( length(listF)-1 listF ) ; 0
      

數組 / 向量

數組 和 向量 不常用,了解一下長什么樣就行。

數組(不常用)

  • 定義

    declare

    declare( ARRAY[10] )
    
  • 賦值

    ARRAY[2] = 4
    ARRAY[3] = 5
    
  • 引用

    println( ARRAY[2] * ARRAY[3] )
    ; 20
    println( ARRAY[0] )
    ; unbound
    println( ARRAY[11] )
    ; *Error* arrayref: array index out of bounds - ARRAY[11]
    

向量(不常用)

  • 定義

    makeVector

    VECTOR = makeVector( 10 )
    
  • 賦值

    VECTOR[2] = 4
    VECTOR[3] = 5
    
  • 引用

    println( VECTOR[2] * VECTOR[3] )
    ; 20
    println( VECTOR[0] )
    ; unbound
    println( VECTOR[11] )
    ; *Error* arrayref: array index out of bounds - VECTOR[11]
    

對照表 / 哈希

某些情況下可以使用哈希來優化代碼邏輯、運行速度等等。

  • 定義

    makeTable

    HASH = makeTable( 'HASH )
    
  • 賦值

    HASH[1] = "ONE"
    HASH["2"] = 2
    HASH[cvId] = "cellName"
    
  • 引用

    • 查看一個哈希的所有 key / value

      HASH~>?
      ; ( "2" 1 db:0x21cfda1a )
      HASH~>??
      ; ( "2" 2 1 "ONE" db:0x21cfda1a "cellName" )
      
    • 遍歷一個哈希

      foreach( key HASH
        printf( "key: %A  ,  value: %A\n" key HASH[key] )
      )
      ; key: db:0x21cfda1a  ,  value: "cellName"
      ; key: 1  ,  value: "ONE"
      ; key: "2"  ,  value: 2
      

條件判斷

if

  • 真/假 情況運行的都是單一的語句

    if( 條件
      當條件成立時運行
      當條件不成立時運行
    )
    

    例如:當 a > b 成立時,打印 "Yes";不成立時,打印時 "No"。

    if( a > b
      println( "Yes" )
      println( "No"  )
    )
    
  • 真/假 情況需要運行多條語句

    if( 條件
    then
      當條件成立時運行 1
      當條件成立時運行 2
    else
      當條件不成立時運行 1
      當條件不成立時運行 2
    )
    

    例如:當 a > b 成立時,c 賦值 1 然后打印 "Yes";不成立時,c 賦值為 0 然后打印時 "No"。

    if( a > b
    then
      c = 1
      println( "Yes" )
    else
      c = 0
      println( "No"  )
    )
    
  • 多層嵌套

    Skill 不支持 if-elsif-else 這種簡化的寫法,但是邏輯是一樣的,后級的 if 需要在上一級的 else中。

    例如:

    • 當 a > b 成立時,打印 "Yes";
    • 否則當 a > c 成立時,打印 "Yes";
    • 都不成立時,打印時 "No"。
    if( a > b
      println( "Yes" )
      if( a > c
        println( "Yes" )
        println( "No"  )
      )
    )
    

    第二個 if 雖然不是 單一語句,但可以將整個 if( a>c ... ) 看做一個整體,所以也可以忽略 then/else

    這就存在一個問題,當需要判斷多個條件的時候 if 的寫法就不太好看了,這時就可以使用 case 或者 cond ,后面會講。

when / unless

when / unless 就非常簡單了,只當給定的條件為 / 的時候才運行給定的語句

條件為真才運行。

when( a > b
  println( "Yes" )
)

條件為假才運行。

unless( a > b
  println( "No" )
)

case / cond

前面說了 case/cond 可以用來優化多條件下的 if ,因此邏輯是一樣的。

  • case

    當所有的條件都是對一個變量做是否相等的判斷的時候,可以使用 case

    例如,現在有一個變量 arg:

    • 當 arg 等於 "a" 時,打印 "Is a";
    • 否則當 arg 等於 "b" 或 "c" 時,打印 "Is b or c";
    • 否則當 arg 等於 "d" 時,打印 "Is d";
    • 全都不成立時,打印 "Unmatch"。
    case( arg
        ( "a"
            println( "Is a" )
        )
        (( "b" "c" )
            println( "Is b or c" )
        )
        ( "d"
            println( "Is d" )
        )
        ( t
            println( "Unmatch" )
        )
    )
    

    上面的語句換成 if 需要這樣寫:

    if( arg == "a"
        println( "Is a" )
        if( arg == "b" || arg == "c"
            println( "Is b or c" )
            if( arg == "d"
                println( "Is d" )
                println( "Unmatch" )
            )
        )
    )
    

    很明顯 case 的寫法更加清晰,觀感上也更加舒服。

  • cond

    case 的使用情景比較單一,但條件多且判斷的對象或者邏輯不唯一的時候可以 cond

    例如,現在需要對變量 a 做幾個判斷:

    • 當 a > b 時,打印 "Bigger than b";
    • 否則當 a > c 時,打印 "Bigger than c";
    • 都不成立時,打印 "Smallest"。
    cond(
        ( a > b
            println( "Bigger than b" )
        )
        ( a > c
            println( "Bigger than c" )
        )
        ( t
            println( "Smallest" )
        )
    )
    

循環控制

for

指定一個起始整數(initialValue)和終止整數(finalValue),依次遍歷從 initial 到 final 組成的 list 的每一個元素,間隔為 1。

下面用 for 打印從 0 到 2:

for( x 0 2
    println( x )
)
; 0
; 1
; 2

foreach

指定一個 list ,依此遍歷每一個元素。

下面用 foreach 打印從 0 到 2:

foreach( x list( 0 1 2 )
    println( x )
)
; 0
; 1
; 2

while

指定一個條件,當條件為真時才會運行,當條件為假時跳出 while 循環。

下面用 while 打印從 0 到 2:

a = 0
while( a < 3
    println( a )
    a++
)
; 0
; 1
; 2

運算

算數運算

操作符 函數
+ a + b plus plus( a b )
- a - b difference difference( a b )
* a * b times times( a b )
/ a / b quotient quotient( a b )
remainder remainder( a b )
乘方 ** a ** b expt expt( a b )
開方 sqrt sqrt( a b )

賦值運算

操作符 函數
直接賦值 = a = 1 setq setq( a 1 )
自增 += a += 1
自增(+1) ++ a ++ add1 add1( a )
自減 -= a -= 1
自減(+1) -- a -- sub1 sub1( a )

比較運算

操作符 函數
相等 == a == b equal equal( a b )
不等 != a != b nequal nequal( a b )
小於 < a < b lessp lessp( a b )
小於等於 <= a <= b leqp equal( a b )
大於 > a > b greaterp greaterp( a b )
大於等於 >= a >= b geqp equal( a b )
幾乎相等 nearlyEqual nearlyEqual( a b )

邏輯運算

操作符 函數
&& a && b and and( a b )
|| a || b or or( a b )
! ! a not not( a )

子程序

定義子程序

調用子程序

局部變量

輸入參數

輸入類型限制

可選的輸入參數


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM