HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2016
0904
掘木hspcmpによるコードジェネレート過程を覗いてみた5解決


掘木

リンク

2016/9/4(Sun) 11:17:17|NO.76787

openHSPのhspcmpを解読していたところ、
個人的にいろいろびっくりな仕様が見つかったので適当に流してみる。(16/08/13時点での内容です)
長くHSPやってる人からすると当然な物ばっかりでしょうか…

# \ define EXTREAMHSP // #は区切り文字であり、指令名称(define等)は連結する必要がない。 #const A2 100feetf// 定数宣言は数値の後ろに不要な文字を含んでも、 // 使用可能文字の後ろが行末なら解釈可能。 mes A2 // 表示内容は100でしょうね。 mes "<<-複行記述とdefine---------------->>" #define MLSBEGIN {" // 複行開始 #define MLSEND "} // 複行終了 mes MLSBEGIN あああ MLSEND // プリプロセスは置き換えと文字列句、コメント除去を mes {"あああ"} // 同時に行うが、複行文字列を一つの句として認識する機能はない。 mes "<<-空文置き換えdefine---------------->>" // Assertマクロなどで利用される、パラメータを持つdefineは、 // 置換結果に何かしらの非空白文字が必要。 #define TESTRUN(%1) // コメントの付与することでコンパイルエラーを回避できる。 // コメントの有無でコンパイル結果に影響が出るってどうなんだろう。 TESTRUN y = 21 : mes y // (正しくyを未初期化変数として、0が表示される) mes "<<-defineとconstの挙動差異---------------->>" #define FLOAT100DEF 100f // プリプロセスとコードジェネレートで #const FLOAT100CONST 100f // サフィックスの処理が異なるため、同一の値が出る保証はない。 mes FLOAT100DEF mes FLOAT100CONST #const int VALTEST 5/2+5/2 // const内部は実行時演算と異なり、演算中の型は実数型で固定される。 mes "<<-プレフィックスと整数変換---------------->>" mes $01_00 // 2進、16進プレフィックスでは、数値の間にインデント代わりに_を使えるらしい。 mes int("$01_00") mes int("$01\n") // 実行時の数値変換においては、非数値はすべて0として扱われるらしい。 // openHSP内のsupio、htoiにてそのように実装されている。 // 波及先が広すぎて訂正可能な代物か不明。 // なお、2進プレフィックスである"%"は実行時変換に対応していない。 // 当然ながら、プリプロセス時に使っている変換ルーチンは別に存在する。 // そしてコンパイル時に使っている変換ルーチンはそれはそれで存在する。 mes "<<-トークン分割とプレフィックス---------------->>" mes 0x // 2進、16進プレフィックスは後続に解釈可能文字がある必要がない。 mes 0xor(5) // 解釈不能文字が現れたらそこでトークンを切るため、これは 0x or (5) だ。 mes 3xor(5) // これは 3 xor (5) で 6 になる。 mes "<<-defineのデフォルト引数---------------->>" #define CS1(%1=0x10) "%1 !"// defineで文字列置換するとわかるが、 #define CS2(%1=1e3) "%1 !" // デフォルト引数は整数、実数で展開方法が異なる。 mes CS1 0x10 // mes CS1 // 整数のデフォルトは10進数値に展開される。 mes CS2 1e3 // 1e3を通常にmesに送ると 1000.000000 と出力される。 mes CS2 // 実数のデフォルトはその文字そのままである。 // なお、パラメータ置き換えはdefineされたトークンの末端から解釈されるため、 // CS1 0x10 は、 " 0x10" と展開されていることが分かる。 // (前方の空白は除去しないが、後続の空白は除去される。) #define ADD11 1+1 #define DFT(%1=ADD11) %1 // デフォルト引数に負定数を指定することはできるが、 // 演算式を入れることはできない。%1=-2 は可 %1=1+1 、 %1=-__line__ は不可。 // だが、defineで演算式に置換する名前を用意すればコンパイルが通る。当然実行時解釈。 mes DFT mes "<<-値表現の限界---------------->>" mes abs(0x80000000) // 絶対値関数は非負の値を返すことを保証しない。(MSの仕様と一致) mes 0x1010_1010_1010_1010 // 表現域外の値を入れても警告が出るわけではない。 mes 3000000000 // mes "<<----------------->>"
どうにもhspcmpさんは生成される実行コードのへの最適化がほぼ(というか全く?)ないようなので、
少し手を加えてみようかなと思って覗いてみたらコレダヨ…。

今もなお、いくつかの警告を増やしてリファクタリングしつつ解読中…もはや原型がない…。
それなりに動いても、コンパイル速度が犠牲になるし、改変影響テストもできそうにないので
そのまま闇に葬り去ることになりそうですん。。。



この記事に返信する


へび

リンク

2016/9/6(Tue) 06:25:10|NO.76798

おもしろいですね



Y_repeat

リンク

2016/9/8(Thu) 06:18:36|NO.76813

>今もなお、いくつかの警告を増やしてリファクタリングしつつ解読中…もはや原型がない…。
原型がなくても堀木さんがリファクタしたソースコードも見てみたいですね
気が向きましたら このスレに貼ってみるのも一案だと思います



掘木

リンク

2016/9/10(Sat) 21:52:54|NO.76851

むー…ドットを使うと字句解釈がプリプロセスとコードジェネレートで違ってくるみたい。

arr.0x01 // pre[ arr . 0x01 ] comp[ arr . 0x01 ] -> arr(1) arr.0.0x01 // pre[ arr . 0.0 x01 ] comp[ arr . 0 . 0x01 ] -> arr(0,1) #const x01 (-8) arr.0x01 // pre[ arr . 0x01 ] comp[ arr . 0x01 ] -> arr(1) arr.0.0x01 // pre[ arr . 0.0 x01 ] comp[ arr . 0 . 0 -8 ] -> arr(0,0)-8
#defineや#constの置き換えが適切な字句単位で置き換わらない以上、
あまり拘ってもしょうがないのかなあ。
(constの置き換えで単一の数値句を形成するように思えるけど、演算子と数値句に分離してるし。)



掘木

リンク

2016/9/13(Tue) 23:52:23|NO.76874

同一挙動を目指すリファクタリングに限界を感じてきた。
摩訶不思議な挙動を正確にトレースしていくのはかなり辛い。というか無理。

実行テスト用の理想コードがあればそれを以って実装するんだけども、そんなのあるのだろうか。
β3で"{" "}"を区切り字にした更新を取り消してる以上テストケースが何かあると思うんだけども…。

正直このスレッドに張ったものの一部は仕様と認めなくないものもあるのだが…
仕様と不具合の線引きが不明なので一概に切り落とせなくて辛い。

ところ変わって、今日見つけたのはマニュアルと見比べた感じ不具合のように思える。

// 半角カタカナ「ヤ」を変数名に使用する。別に変数でなくても関数でも何でもよかったけど。 AヤB = 1 : Aヤb = 2 mes strf("%d %d",AヤB,Aヤb) // 1 2 と表示される Aヤア = 3 : Aヤヂ = 4 mes strf("%d %d",Aヤア,Aヤヂ) // 4 4 と表示される
どうやら半角カタカナ(文字コード0x80以上の非全角文字)が紛れ込むと大文字小文字の判定誤認が起こる模様。
原因はhspcmp/label.cppのCLabel::StrCase内に潜んでるように見える。
(なお、StrCaseはハッシュ値返すメソッドです。名前から実装を推測してはいけない(戒め)。確実に嵌ります。)

文字セット依存のコードが随所に点在している故こんなことに…。
UTF-8環境でも同様のミスコードを生成しそうな気もする。。。実際どうなんだろう?



掘木

リンク

2016/10/6(Thu) 22:02:58|NO.77055

twのほうでも通知したけども…

現状、未初期化変数の検出機能は、単一の文の解釈の前に実行され、
・既定の命令の第一引数に指定されている。
・文の先頭に当たるvariantトークンとなった。
にて判断される模様。文頭になるケースは単純代入以外にもあるので…

p1 ++ // インクリメントは初期化扱い。 p2 !=== "6" // 複合代入演算子も初期化扱い。この複合演算子とこの記述のメリットが分からない。 p3 | p2 // hspは(非推奨)ながらも代入演算子を省略できる。非推奨でも警告一つ出さない。 p4 = limit(p4,p4,p4) // p4は初期化済みらしい。じゃあ演算で出てくるp4は何を指す?
どういう意図で初期化済みなのだろう…うーん?良く分からない。


もう一点不可解なのが、命令と関数での呼び出し間違い。

limit p1,2,5 mes F_F(20,20) F_C 20,20 mes mes("あああ") stop #defcfunc F_C int it1,int it2 return #deffunc F_F int it1,int it2 return
構文(文法)エラー(syntax error)が実行時エラー?コンパイラが発するエラーなのでは?
コンパイルが許しているのだから、実行側が何かしらの追加仕様を見送っていると考えるべきなのかな。
(CLabelにユーザー命令/関数が一緒くたにMODCMDで放り込まれ、
 トークンがLABELかVARかそれ以外かという判断の時点で関数/命令の区別は消失している。)

このあたりの考えを聞いてみたい…。(言語実装仕様でも使い勝手の面からでも)



ONION software Copyright 1997-2023(c) All rights reserved.