- 追加された行はこの色です。
- 削除された行はこの色です。
#freeze
[[FrontPage]]
2008/07/07からのアクセス回数 &counter;
#contents
[[antlr/構文木を使った解析]]では、構文木を使った四則演算プログラムを紹介しましたが、
ここでは[[Cコンパイラ設計(yacc・lexの応用)/第7章 コードの生成]]と同じアセンブラ
コードを出力するコンパイラを作成します。
** SampleC構文解析 [#f916548a]
SampleCは、Cのサブセットで
- 型はintのスカラーのみ
- フロー制御は、if文、while文のみ
をサポートしています。
最初に、sample.yをベースに以下の処理をします。
- 左再帰をEBNFに変更
- 優先順位を明示的に記述
- エラー処理を外す
*** 生成する構文木の形 [#zca43761]
入力として以下のデータを入力すると
#pre{{
main()
{
gcd(36,54);
}
gcd(a,b)
int a;
int b;
{
if (a == b)
return a;
else if (a > b)
return gcd(a-b, b);
else
return gcd(a, b-a);
}
}}
nilノードの下にPROGRAMノードがあり、その下に2個のFUNCノードとCALLノードを持つツリーが作成されます。
#ref(func_tree.jpg);
1個目のFUNCノードは、
#ref(func_tree1.jpg);
2個目のFUNCノードは、
#ref(func_tree2.jpg);
のようになります。
- FUNCノードには、以下のノードが付いています。
-- 関数の型
-- 関数名
-- 関数パラメータのFUNCPARノード
-- パラメータの型定義のPARDEFノード
-- 関数本体のBLOCKノード
- 関数呼び出しCALLノードには、以下のノードが付いています。
-- 関数名
-- 引数リストのARGノード
- IF文のIFノードには、以下のノードが付いています。
-- 条件文
-- 条件がTRUEの時の処理ノード
-- 条件がFALSEの時の処理ノード
また、SampleCの構文木のみの特徴として式の後にセミコロンがきた
タイミングでスタックをクリアするためにEXPRとCLEARノードを追加します。
例えば、
1 + 2;
は、
^(EXPR ^(ALU_ADD 1 2) CLEAR)
となります。
*** SampleC.g [#zb4c1cd3]
SampleC.gは次のようになります。
#pre{{
grammar SampleC;
options{
output = AST;
ASTLabelType = CommonTree;
}
tokens{
PROGRAM; FUNC; FUNCPAR; PARDEF; VARDEF; BLOCK; ASSIGN;
CALL; NOT; NEGATE; ARG; NUM; VAR; PREFIX; NOP; CLEAR;
ALU_ADD; ALU_SUB; ALU_MUL; ALU_DIV; ALU_LT;
ALU_GT; ALU_LE; ALU_GE; ALU_EQ; ALU_NE;
ALU_AND; ALU_OR; ALU_XOR;
INT; VOID; IF; WHILE; EXPR;
}
program
: (
d=definition
{ if ($d.tree != null) System.out.println($d.tree.toStringTree());}
)+ EOF
-> ^(PROGRAM definition+ ^(CALL {adaptor.create(IDENTIFIER,"main")} ^(ARG)))
;
definition
: functionDefintion
-> functionDefintion
| declaration
-> declaration
;
functionDefintion
: funcType IDENTIFIER '(' parameterList ')' parameterDeclarations compoundStatement
-> ^(FUNC funcType IDENTIFIER ^(FUNCPAR parameterList) parameterDeclarations compoundStatement)
;
funcType
: type
-> type
| -> INT
;
parameterList
: (IDENTIFIER (',' IDENTIFIER)*)?
-> IDENTIFIER*
;
parameterDeclarations
: parameterDeclaration*
-> ^(PARDEF parameterDeclaration*)
;
parameterDeclaration
: (t=type id=IDENTIFIER
-> ^(VARDEF $t $id)
)
(',' (id=IDENTIFIER)
-> $parameterDeclaration ^(VARDEF $t $id)
)* ';'
;
compoundStatement
: '{' declarations statements '}'
-> ^(BLOCK declarations statements)
;
declarations
: declaration*
;
declaration
: (t=type id=IDENTIFIER
-> ^(VARDEF $t $id)
)
(',' (id=IDENTIFIER)
-> $declaration ^(VARDEF $t $id)
)* ';'
;
statements
: statement*
;
statement
options {
backtrack=true;
}
: e=expression ';'
-> ^(EXPR $e CLEAR)
| ';'
-> NOP
| BREAK
-> BREAK
| CONTINUE
-> CONTINUE
| RETURN ';'
-> RETURN
| RETURN e=expression ';'
-> ^(RETURN $e)
| c=compoundStatement
-> $c
| ifstm
-> ifstm
| 'while' '(' e=expression ')' s=statement
-> ^(WHILE $e $s)
;
ifstm
options{
backtrack=true;
}
: 'if' '(' e=expression ')' s1=statement ELSE s2=statement
-> ^(IF $e $s1 $s2)
| 'if' '(' e=expression ')' s1=statement
-> ^(IF $e $s1 NOP)
;
expression
: binary
-> binary
;
binary
: i=IDENTIFIER '=' e=expression
-> ^(ASSIGN $i $e)
| IDENTIFIER PE e=expression
-> ^(ASSIGN IDENTIFIER ^(ALU_ADD IDENTIFIER $e))
| IDENTIFIER ME e=expression
-> ^(ASSIGN IDENTIFIER ^(ALU_SUB IDENTIFIER $e))
| IDENTIFIER TE e=expression
-> ^(ASSIGN IDENTIFIER ^(ALU_MUL IDENTIFIER $e))
| IDENTIFIER DE e=expression
-> ^(ASSIGN IDENTIFIER ^(ALU_DIV IDENTIFIER $e))
| equalityExpr
-> equalityExpr
;
equalityExpr
: comparisonExpr (eqOp^ comparisonExpr)*
;
eqOp
: '=='
-> ALU_EQ
| ('!=' | '<>')
-> ALU_NE
;
comparisonExpr
: additiveExpr (compOp^ additiveExpr)*
;
compOp
: '>'
-> ALU_GT
| '<'
-> ALU_LT
| '<='
-> ALU_LE
| '>='
-> ALU_GE
;
additiveExpr
: multiplicativeExpr (addOp^ multiplicativeExpr)*
;
addOp
: '+'
-> ALU_ADD
| '-'
-> ALU_SUB
;
multiplicativeExpr
: bitExpr (mulOp^ bitExpr)*
;
mulOp
: '*'
-> ALU_MUL
| '/'
-> ALU_DIV
;
bitExpr
: notExpr (bitOp^ notExpr)*
;
bitOp
: '&'
-> ALU_AND
| '|'
-> ALU_OR
| '^'
-> ALU_XOR
;
notExpr
: op='!'? negationExpr
-> {$op != null}? ^(NOT negationExpr)
-> negationExpr
;
negationExpr
: op='-'? primary
-> {$op != null}? ^(NEGATE primary)
-> primary
;
primary
: atom
| '('! expression ')'!
;
atom
: IDENTIFIER
-> IDENTIFIER
| PP IDENTIFIER
-> ^(PREFIX IDENTIFIER PP)
| MM IDENTIFIER
-> ^(PREFIX IDENTIFIER MM)
| CONSTANT
-> CONSTANT
| IDENTIFIER '(' a=argumentList ')'
-> ^(CALL IDENTIFIER $a)
;
argumentList
: (expression (',' expression)*)?
-> ^(ARG expression*)
;
type
: 'int'
-> INT
;
BREAK : 'break' ;
CONTINUE : 'continue' ;
RETURN : 'return' ;
ELSE : 'else' ;
PP : '++' ;
MM : '--' ;
PE : '+=' ;
ME : '-=' ;
TE : '*=' ;
DE : '/=' ;
IDENTIFIER : ('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z'|'0'..'9')* ;
CONSTANT : '0'..'9'+ ;
WS : (' '|'\t'|'\r'|'\n')+ {skip();} ;
}}
** メモリ管理 [#p5f434d2]
Cでは、広域変数領域、パラメータ領域、局所領域の変数を管理するため、それぞれオフセットを保持する必要があります。
下記のプログラムの場合、
#pre{{
main(p0, p1)
int p0;
int p1;
{ int main0, main1;
p0, p1, main0,main1;
{ int nest2, next3, nest4;
nest2,next3,nest4;
}
{ int new2;
new2;
{ int inner3;
inner3;
}
{ int inner3, inner4;
inner3,inner4;
}
}
{ int last2;
last2;
}
}
}}
各変数は、次のようにメモリに割り当てられます。
#ref(variable_region.jpg);
実際にSampleCのgeneratorの出力のloadオペレータの後の、par, lclの後の数値がそれぞれの変数領域
のオフセットを表しています。上記の図と一致していることが分かります。
#pre{{
main entry $$1
load par,0 ; p0
pop ; discard
load par,1 ; p1
pop ; discard
load lcl,0 ; main0
pop ; discard
load lcl,1 ; main1
pop ; clear stack
load lcl,2 ; nest2
pop ; discard
load lcl,3 ; next3
pop ; discard
load lcl,4 ; nest4
pop ; clear stack
load lcl,2 ; new2
pop ; clear stack
load lcl,3 ; inner3
pop ; clear stack
load lcl,3 ; inner3
pop ; discard
load lcl,4 ; inner4
pop ; clear stack
load lcl,2 ; last2
pop ; clear stack
return ; end of function
$$1 equ= 5 ; main
end 1,main
}}
*** SymbolTable [#sdad0c03]
SampleCに合わせて、関数や変数を管理するSymbolTalbeを作成します。
- 関数を管理するfunctions
- 変数は、リストscopesを使って関数呼び出し、複合文に合わせてパラメータ、ローカル変数マップを管理
- scopesの0番目には広域変数を管理するglobalsマップがあり、1番目には関数のパラメータを管理する
paramsマップがあります。
- 変数名をscopesの最後から順に検索し、メモリ割り当て領域を求めます。
SymbolTable.javaは、次のようになります。
#pre{{
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SymbolTable {
static final int GLOBALS = 0;
static final int PARAMS = 1;
Map functions = new HashMap();
List scopes = new ArrayList();
Map globals = new HashMap();
Map params = new HashMap();
int globalVarNum = 1;
int paramVarNum = 0;
int localVarNum = 0;
int LabelNum = 0;
public SymbolTable() {
scopes.add(globals);
scopes.add(params);
}
public int nextLabelNum() {
return (LabelNum++);
}
public void enterFunction(String name) {
enterBlock();
}
public void exitFunction() {
exitBlock();
params.clear();
}
public void enterBlock() {
scopes.add(new HashMap());
}
public void exitBlock() {
int lastIndex = scopes.size()-1;
if (lastIndex > PARAMS) {
Map scope = (Map)scopes.get(lastIndex);
localVarNum -= scope.size();
scopes.remove(lastIndex);
}
}
public void declareFunction(String name, String returnType) {
functions.put(name, new Function(name, returnType));
}
public void declareGlobalVariable(String name, String type) {
globals.put(name, new Variable(name, type, new Integer(globalVarNum++)));
}
public void declareLocalVariable(String name, String type) {
Map scope = (Map)scopes.get(scopes.size()-1);
scope.put(name, new Variable(name, type, new Integer(localVarNum++)));
}
public void declareParameter(String name, String type) {
params.put(name, new Variable(name, type, new Integer(paramVarNum++)));
}
public boolean isDeclaredVariable(String name) {
return (getVariableModeAndOffset(name) != null);
}
public boolean isDeclaredFunction(String name) {
return (functions.get(name) != null);
}
public String getVariableModeAndOffset(String name) {
for (int i = scopes.size()-1; i >= 0; i--) {
Map vars = (Map)scopes.get(i);
Variable v = (Variable)vars.get(name);
if (v != null) {
if (i == GLOBALS) return ("gbl," + v.offset);
else if (i == PARAMS) return ("par," + v.offset);
else return ("lcl," + v.offset);
}
}
return (null);
}
}
class Variable {
public String name;
public String type;
public Integer offset;
public Variable(String name, String type, Integer offset) {
this.name = name;
this.type = type;
this.offset = offset;
}
}
class Function {
public String name;
public String returnType;
public Function(String name, String returnType) {
this.name = name;
this.returnType = returnType;
}
}}}
** コード生成
コンパイルによって生成されるアセンブラとその関数もSampleCの関数名と同じ名称でGen.javaに定義します。
Gen.javaのソースは以下の通りです。
#pre{{
import java.util.Stack;
public class Gen {
public static final String OP_ALU = "alu";
public static final String OP_DEC = "dec";
public static final String OP_INC = "inc";
public static final String OP_LOAD = "load";
public static final String OP_STORE = "store";
public static final String OP_POP = "pop";
public static final String OP_JUMPZ = "jumpz";
public static final String OP_JUMP = "jump";
public static final String OP_CALL = "call";
public static final String OP_ENTRY = "entry";
public static final String OP_RETURN = "return";
public static final String MOD_IMMED = "con";
public static final String ALU_ADD = "+";
public static final String ALU_SUB = "-";
public static final String ALU_MUL = "*";
public static final String ALU_DIV = "/";
public static final String ALU_MOD = "%";
public static final String ALU_LT = "<";
public static final String ALU_GT = ">";
public static final String ALU_LE = "<=";
public static final String ALU_GE = ">=";
public static final String ALU_EQ = "==";
public static final String ALU_NE = "!=";
public static final String ALU_AND = "&";
public static final String ALU_OR = "|";
public static final String ALU_XOR = "^";
private Stack b_stack = new Stack();
private Stack c_stack = new Stack();
private String c_top = null;
private String b_top = null;
private int new_label = 0;
public String new_label() {
return ("$$" + (++new_label));
}
public void gen_alu(String mod, String comment) {
System.out.println("\t" + OP_ALU + "\t" + mod + "\t\t; " + comment);
}
public void gen_li(String constant) {
System.out.println("\t" + OP_LOAD + "\t" + MOD_IMMED + "," + constant);
}
public void gen(String op, String modAndOffset, String comment) {
System.out.println("\t" + op + "\t" + modAndOffset + "\t\t; " + comment);
}
public void gen_pr(String op, String comment) {
System.out.println("\t" + op + "\t\t\t; " + comment);
}
public String gen_jump(String op, String label, String comment) {
System.out.println("\t" + op + "\t" + label + "\t\t; " + comment);
return label;
}
public String gen_label(String label) {
System.out.println(label + "\tequ\t*");
return label;
}
public void gen_break() {
gen_jump(OP_JUMP, b_top, "BREAK");
}
public void gen_continue() {
gen_jump(OP_JUMP, b_top, "CONTINUE");
}
public void gen_call(String name, int count) {
System.out.println("\t" + OP_CALL + "\t" + count + "," + name);
for (int i = 0; i < count; i++)
gen_pr(OP_POP, "pop argument");
gen(OP_LOAD, "gbl,0", "push result");
}
public String gen_entry(String name) {
String label = new_label();
System.out.println(name + "\t" + OP_ENTRY + "\t" + label);
return label;
}
public void fix_entry(String name, String label) {
System.out.println(label + "\tequ=\tx\t\t; " + name);
}
public void end_program() {
System.out.println("\tend,x\tmain");
}
public void push_break(String label) {
b_stack.push(label);
}
public void push_continue(String label) {
c_stack.push(label);
}
public void pop_break() {
b_top = (String)b_stack.pop();
}
public void pop_continue() {
c_top = (String)c_stack.pop();
}
}
}}
** 構文木ツリーからのコンパイル [#jcceca76]
次に、SampleC.gによって生成された構文木からアセンブラを生成するCompiler.gを作成
します。
ANTLRでは、テンプレート機能を使ってコード生成をすることができますが、
SampleC.yと比較できるように、ここではMain.genにセットされているGenクラスの
メソッド呼び出しでアセンブラを出力します。
SampleC.gの以下のようになります。
#pre{{
tree grammar Compiler;
options{
tokenVocab = SampleC;
ASTLabelType = CommonTree;
}
program
@after{
Main.gen.end_program();
}
: ^(PROGRAM definition+ call) EOF
;
definition
: functionDefintion
| ^(VARDEF INT i=IDENTIFIER)
{ Main.symtab.declareGlobalVariable($i.text, "int"); }
;
functionDefintion
scope {
String label;
}
@after{
Main.symtab.exitFunction();
}
: ^(FUNC INT i=IDENTIFIER ^(FUNCPAR parameterList) parameterDeclarations
{ Main.symtab.declareFunction($i.text, "int");
Main.symtab.enterFunction($i.text);
$functionDefintion::label = Main.gen.gen_entry($i.text);
}
compoundStatement
{ Main.gen.gen_pr(Gen.OP_RETURN, "end of function");
Main.gen.fix_entry($i.text, $functionDefintion::label);
}
)
;
parameterList
: IDENTIFIER*
;
parameterDeclarations
: ^(PARDEF parameterDeclaration*)
;
parameterDeclaration
: ^(VARDEF INT i=IDENTIFIER
{ Main.symtab.declareParameter($i.text, "int"); }
)
;
compoundStatement
@init{
Main.symtab.enterBlock();
}
@after{
Main.symtab.exitBlock();
}
: ^(BLOCK declarations statements)
;
declarations
: declaration*
;
declaration
: ^(VARDEF INT i=IDENTIFIER
{ Main.symtab.declareLocalVariable($i.text, "int"); }
)
;
statements
: statement*
;
statement
scope {
String label1;
String label2;
}
: e=expression
| ^(EXPR expression CLEAR)
| NOP
| CLEAR
{ Main.gen.gen_pr(Gen.OP_POP, "clear stack"); }
| BREAK
{ Main.gen.gen_break(); }
| CONTINUE
{ Main.gen.gen_continue(); }
| RETURN
{ Main.gen.gen_pr(Gen.OP_RETURN, "RETURN"); }
| ^(RETURN e=expression)
{ Main.gen.gen(Gen.OP_STORE, "gbl,0", "save result");
Main.gen.gen_pr(Gen.OP_RETURN, "RETURN");
}
| c=compoundStatement
| ^(IF
e=expression
{ $statement::label1 = Main.gen.gen_jump(Gen.OP_JUMPZ, Main.gen.new_label(), "IF");
}
s1=statement
{ $statement::label2 = Main.gen.gen_jump(Gen.OP_JUMP, Main.gen.new_label(), "past ELSE");
Main.gen.gen_label($statement::label1);
}
s2=statement
{ Main.gen.gen_label($statement::label2); }
)
| ^(WHILE
{ $statement::label1 = Main.gen.gen_label($statement::label1);
Main.gen.push_continue($statement::label1);
}
e=expression
{ $statement::label2 = Main.gen.gen_jump(Gen.OP_JUMPZ, Main.gen.new_label(), "WHILE");
Main.gen.push_break($statement::label2);
}
s=statement
{ Main.gen.gen_jump(Gen.OP_JUMP, $statement::label1, "repeat WHILE");
Main.gen.gen_label($statement::label2);
Main.gen.pop_break();
Main.gen.pop_continue();
}
)
;
expression
: ^(ALU_EQ e1=expression e2=expression)
{ Main.gen.gen_alu(Gen.ALU_EQ, "=="); }
| ^(ALU_NE e1=expression e2=expression)
{ Main.gen.gen_alu(Gen.ALU_NE, "!="); }
| ^(ALU_GT e1=expression e2=expression)
{ Main.gen.gen_alu(Gen.ALU_GT, ">"); }
| ^(ALU_LT e1=expression e2=expression)
{ Main.gen.gen_alu(Gen.ALU_LT, "<"); }
| ^(ALU_LE e1=expression e2=expression)
{ Main.gen.gen_alu(Gen.ALU_LE, "<="); }
| ^(ALU_GE e1=expression e2=expression)
{ Main.gen.gen_alu(Gen.ALU_GE, ">="); }
| ^(ALU_ADD e1=expression e2=expression)
{ Main.gen.gen_alu(Gen.ALU_ADD, "+"); }
| ^(ALU_SUB e1=expression e2=expression)
{ Main.gen.gen_alu(Gen.ALU_SUB, "-"); }
| ^(ALU_MUL e1=expression e2=expression)
{ Main.gen.gen_alu(Gen.ALU_MUL, "*"); }
| ^(ALU_DIV e1=expression e2=expression)
{ Main.gen.gen_alu(Gen.ALU_DIV, "/"); }
| ^(ALU_AND e1=expression e2=expression)
{ Main.gen.gen_alu(Gen.ALU_AND, "&"); }
| ^(ALU_OR e1=expression e2=expression)
{ Main.gen.gen_alu(Gen.ALU_OR, "|"); }
| ^(ALU_XOR e1=expression e2=expression)
{ Main.gen.gen_alu(Gen.ALU_XOR, "^"); }
| ^(NOT e1=expression)
{ Main.gen.gen_alu(Gen.ALU_EQ, "=="); }
| ^(NEGATE e1=expression)
{ Main.gen.gen_alu(Gen.ALU_EQ, "=="); }
| ^(ASSIGN i=IDENTIFIER e=expression)
{ Main.gen.gen(Gen.OP_STORE, Main.symtab.getVariableModeAndOffset($i.text), $i.text); }
| ^(PREFIX i=IDENTIFIER PP)
{ Main.gen.gen(Gen.OP_INC, Main.symtab.getVariableModeAndOffset($i.text), $i.text); }
| ^(PREFIX i=IDENTIFIER MM)
{ Main.gen.gen(Gen.OP_DEC, Main.symtab.getVariableModeAndOffset($i.text), $i.text); }
| call
| i=IDENTIFIER
{ Main.gen.gen(Gen.OP_LOAD, Main.symtab.getVariableModeAndOffset($i.text), $i.text); }
| c=CONSTANT
{ Main.gen.gen_li($c.text); }
;
call
@init {
int argCnt = 0;
}
: ^(CALL i=IDENTIFIER
{ argCnt = 0; }
^(ARG (e=expression
{ argCnt++; }
)*)
{ Main.gen.gen_call($i.text, argCnt); }
)
;
}}
** 例 [#jedb0887]
Main関数は、以下のように定義します。
#pre{{
import org.antlr.runtime.ANTLRInputStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.CommonTreeNodeStream;
public class Main {
public static SymbolTable symtab = new SymbolTable();
public static Gen gen = new Gen();
public static void main(String[] args) throws Exception {
ANTLRInputStream input = new ANTLRInputStream(System.in);
SampleCLexer lexer = new SampleCLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
SampleCParser parser = new SampleCParser(tokens);
SampleCParser.program_return r = parser.program();
CommonTree t = (CommonTree)r.getTree();
CommonTreeNodeStream nodes = new CommonTreeNodeStream(t);
Compiler walker = new Compiler(nodes);
walker.program();
}
}
}}
*** テストラン [#ub00547c]
プログラムは、変数や関数のチェックもありませんが、SampleCの例と同じ、以下の入力データを与えると
#pre{{
main()
{
gcd(36,54);
}
gcd(a,b)
int a;
int b;
{
if (a == b)
return a;
else if (a > b)
return gcd(a-b, b);
else
return gcd(a, b-a);
}
}}
以下のように結果を出力しました(BLOCKを見やすく手で修正してあります)。
#pre{{
(FUNC INT main FUNCPAR PARDEF (BLOCK (EXPR (CALL gcd (ARG 36 54)) CLEAR)))
(FUNC INT gcd (FUNCPAR a b) (PARDEF (VARDEF INT a) (VARDEF INT b))
(BLOCK
(IF (ALU_EQ a b)
(return a)
(IF (ALU_GT a b)
(return (CALL gcd (ARG (ALU_SUB a b) b)))
(return (CALL gcd (ARG a (ALU_SUB b a))))))))
main entry $$1
load con,36
load con,54
call 2,gcd
pop ; pop argument
pop ; pop argument
load gbl,0 ; push result
return ; end of function
$$1 equ= x ; main
gcd entry $$2
load par,0 ; a
load par,1 ; b
alu == ; ==
jumpz $$3 ; IF
load par,0 ; a
store gbl,0 ; save result
return ; RETURN
jump $$4 ; past ELSE
$$3 equ *
load par,0 ; a
load par,1 ; b
alu > ; >
jumpz $$5 ; IF
load par,0 ; a
load par,1 ; b
alu - ; -
load par,1 ; b
call 2,gcd
pop ; pop argument
pop ; pop argument
load gbl,0 ; push result
store gbl,0 ; save result
return ; RETURN
jump $$6 ; past ELSE
$$5 equ *
load par,0 ; a
load par,1 ; b
load par,0 ; a
alu - ; -
call 2,gcd
pop ; pop argument
pop ; pop argument
load gbl,0 ; push result
store gbl,0 ; save result
return ; RETURN
$$6 equ *
$$4 equ *
return ; end of function
$$2 equ= x ; gcd
call 0,main
load gbl,0 ; push result
end,x main
}}
*** 感想 [#fecc8129]
SampleCコンパイラの移植を通じてANTLRとANTLRWorksの完成度の高さに感動しました。
わずか1日で、SampleCのコンパイラができたことは、驚異のツールと言って過言ではないと
思います。
** コメント [#b42076b0]
この記事は、
#vote(おもしろかった[1],そうでもない[0],わかりずらい[0])
#vote(おもしろかった[27],そうでもない[0],わかりずらい[0])
皆様のご意見、ご希望をお待ちしております。
- インタプリタの作成できづいたEXPRの導入とmain関数呼び出しを追加 -- [[竹本 浩]] &new{2008-07-11 (金) 11:11:06};
- RlrLAuYQtNFOrsG -- [[ruzugoadgcf]] &new{2008-09-11 (木) 15:49:42};
- xHotZSXrrKEKGAZDynk -- [[crugwwkufm]] &new{2008-09-11 (木) 15:49:44};
- 非常に遅くなりましたが…例を作ってくださってありがとうございます。参考にさせていただきます。 -- [[kappa]] &new{2010-08-27 (金) 16:14:04};
#comment
#comment_kcaptcha
}}