2007-09-28
Lysee 的语法定义
关键字: Lysee 开发
很高兴今天能发布这个声明:
经过不懈努力,Lysee于今晨8:30释出1.1.0.5511版本,新版本初步实现了语法定义,帮助Lysee又向前“挪了”一步!
首先向Scheme/Lisp、Haskell、Perl、Python还有其它语言表示一下尊敬:
“syntax”是为Lysee新定义的关键字,用于向语法分析器提示有必要在后续的代码中检测并使用新定义的语法。
用“syntax”定义的语法只是一个再普通不过的函数,下面是前面定义的“thanks”语法的伪代码:
原理:
编译器将函数定义中%(ID)和%{ID}格式中的标识符登记为variant类型的参数,其它符号作为占位符被抛掉。
%(ID) - 表示直接计算%(ID)所在位置的表达式,仅将结果传递给新函数。
%{ID} - 表示在编译时将%{ID}对应位置上的表达式转换为闭包。
下面定义一个略微复杂的语法,实现Pascal中的repeat ... until循环:
看到这儿,研究FP的朋友们应该可以会心的笑了,看看下面的伪代码就更清楚了
再举一个常见的for_each语法定义,真是很简单:
语法定义扎根在函数闭包上,怎么优化函数闭包的生成、使用和释放,如何节省系统资源仍然还是个大问题。
总结一下:语法定义是个好东西,但使用的代价可能不菲,特别是过分滥用时会破坏我们已有的代码。
经过不懈努力,Lysee于今晨8:30释出1.1.0.5511版本,新版本初步实现了语法定义,帮助Lysee又向前“挪了”一步!
首先向Scheme/Lisp、Haskell、Perl、Python还有其它语言表示一下尊敬:
syntax void thanks(%(you) AND %(he))
{
= "thanks!", you, "and", he;
}
thanks "scheme/lisp" AND "haskell, python, perl and others";
“syntax”是为Lysee新定义的关键字,用于向语法分析器提示有必要在后续的代码中检测并使用新定义的语法。
用“syntax”定义的语法只是一个再普通不过的函数,下面是前面定义的“thanks”语法的伪代码:
public variant main(variant ARGS)
{
PUSH_FUNC main::thanks //==> 将函数thanks压栈
PUSH_STR scheme/lisp
PUSH_STR "haskell, python, perl and others"
CALL [3]
POP
RETURN
}
syntax void thanks(%(you) AND %(he)) //==> public void thanks(variant you he)
{
PUSH_STR thanks!
PUSH_VARB you
PUSH_STR and
PUSH_VARB he
PRINT [4]
}
原理:
编译器将函数定义中%(ID)和%{ID}格式中的标识符登记为variant类型的参数,其它符号作为占位符被抛掉。
%(ID) - 表示直接计算%(ID)所在位置的表达式,仅将结果传递给新函数。
%{ID} - 表示在编译时将%{ID}对应位置上的表达式转换为闭包。
下面定义一个略微复杂的语法,实现Pascal中的repeat ... until循环:
// 定义 repeat .. until 语法
syntax void repeat(%(STATEMENT) until %{FALSE})
{
do { STATEMENT() } while (not FALSE());
}
// 使用
int guess, magic = 5;
repeat {
guess = sys::random(10);
= guess, eol;
} until (guess == magic);
// 输出
7
0
3
4
2
5
看到这儿,研究FP的朋友们应该可以会心的笑了,看看下面的伪代码就更清楚了
public variant main(variant ARGS)
{
VARB guess: int
VARB magic: int
PUSH_INT 5
SAVE_TO magic
POP
PUSH_FUNC main::repeat //==> repeat ... until
PUSH_SUBF main::main.1 //==> STATEMENT
PUSH_SUBF main::main.2 //==> FALSE
CALL [3]
POP
RETURN
}
public variant main.1() //==> STATEMENT
{
PUSH_FUNC sys::random
PUSH_INT 10
CALL [2]
SAVE_TO guess
POP
PUSH_VARB guess
PUSH EOL
PRINT [2]
RETURN
}
public variant main.2() //==> FALSE
{
PUSH_VARB guess
PUSH_VARB magic
CALC ==
RETURN [1]
}
syntax void repeat(%(STATEMENT) until %{FALSE})
{
0000:PUSH_VARB STATEMENT //==> main.1
CALL [1]
POP
PUSH_VARB FALSE //==> main.2
CALL [1]
CALC NOT
JMPT 0000: POP
}
再举一个常见的for_each语法定义,真是很简单:
syntax void for_each(%(LIST): %(PROC)) // 要求LIST必须支持each操作
{
LIST.each(PROC);
}
// 调用
for_each strlist("hello\nworld\n!"): {|string item| = item, eol};
// 输出
hello
world
!
语法定义扎根在函数闭包上,怎么优化函数闭包的生成、使用和释放,如何节省系统资源仍然还是个大问题。
总结一下:语法定义是个好东西,但使用的代价可能不菲,特别是过分滥用时会破坏我们已有的代码。
- 09:30
- 浏览 (763)
- 论坛浏览 (1328)
- 评论 (3)
- 相关推荐
评论
下面这段代码是Lysee编译器在发现需要引用新语法时的处理代码:
函数针对前面提到的三种对象进行分别的处理,目标都是调整好堆栈的状态。
在什么时候激发编译器应用新的语法定义要比单纯的解析语法声明过程和上面的堆栈调整过程要复杂的多,代码很难在整体上贴出,抱歉!
procedure TLiParser.ParseExpr_syntax(Expr: TList; EndSyms: TLiSymbols; Syntax: TLiSyntax);
var
index, count: integer;
rec: PLiSyntaxRec;
ask: PLiToken;
begin
ask := CloneSym(FLast);
ask^.Sym := syID;
ask^.Val := Syntax.FullName; //==> 压入语法定义函数
Expr.Add(ask);
count := Syntax.SynRecCount;
index := 0;
while index < count do
begin
rec := Syntax.SynRec[index];
if rec^.si_type = simSymbol then //==> 解析并跳过战位符
begin
SymTestNext([rec^.si_symb]);
if rec^.si_symb = syID then
if FLast^.Val <> rec^.si_text then //==> 标识符区分大小写
Error.SymUnexpected(Self);
end
else
if rec^.si_type = simValue then //==> 直接求值表达式,结果压栈
begin
Inc(index);
if index < count then
begin
rec := Syntax.SynRec[index];
ParseExpr(Expr, [rec^.si_symb], false);
end
else ParseExpr(Expr, EndSyms, false);
end
else
if rec^.si_type = simParcel then //==> 生成闭包后压入栈
begin
Inc(index);
if index < count then
begin
rec := Syntax.SynRec[index];
ParseClosure(KT_VARIANT, '', [rec^.si_symb], rec^.si_text);
end
else ParseClosure(KT_VARIANT, '', EndSyms, '');
end;
Inc(index);
end;
ask := CloneSym(FLast);
ask^.Sym := syAsk; //==> 压入调用参数
ask^.VParamCount := Syntax.ParamCount + 1;
Expr.Add(ask);
if Syntax.LastSynRec^.si_type = simSymbol then // adjust FLast
SymTestNext(EndSyms);
end;
函数针对前面提到的三种对象进行分别的处理,目标都是调整好堆栈的状态。
在什么时候激发编译器应用新的语法定义要比单纯的解析语法声明过程和上面的堆栈调整过程要复杂的多,代码很难在整体上贴出,抱歉!
下面这段代码是Lysee编译器遇到syntax关键字后的解析代码:
从上面的代码可以看出,语法定义需要处理三种不同的对象:
1、占位符 :在Scheme/Lisp中称为关键字,主要用于分隔相临的参数(代码块)
2、表达式 :在语法块外即计算求值的代码块,通常用作流程控制条件。
3、函数闭包:执行具体语句的代码块,是实现语法功能的主体
type
TLiSyntaxItem = (simSymbol, simValue, simParcel);
RLiSyntaxRec = packed record
si_type: TLiSyntaxItem;
si_symb: TLiSymbol;
si_text: string; // set when si_symb is syID
end;
PLiSyntaxRec = ^RLiSyntaxRec;
function TLiSyntax.AddSynRec(Item: TLiSyntaxItem; Sym: TLiSymbol; const Text: string): integer;
var
si: PLiSyntaxRec;
begin
GetMem(si, sizeof(RLiSyntaxRec));
FillChar(si^, sizeof(RLiSyntaxRec), 0);
si^.si_type := Item;
si^.si_symb := Sym;
si^.si_text := Text;
Result := FItems.Add(si);
if Item <> simSymbol then
AddParam(Text, KT_VARIANT);
end;
procedure TLiParser.ParseSyntax;
var
syntax: TLiSyntax;
clss: TLiClass;
last: TLiSyntaxItem;
begin
// 1. parse result type
ParseVarType(clss, false);
// 2. parse syntax name
SymGotoNext;
if not IsPureID(FLast) then
Error.WrongIDName(Self);
if FPage.Declared(FLast.Val) then
Error.Redeclared(Self);
// 3. create syntax object
syntax := TLiSyntax.Create(FLast^.Val, FPage);
syntax.FPos := FLast^.Pos;
syntax.ResultType := clss; //==> 语法块的返回值类型
// 4. parse parametres
SymTestNext([syLParen]); //==> 开始参数解析
SymGotoNext;
if FLast^.Sym = syRParen then
Error.SymUnexpected(Self);
last := simSymbol; //==> 缺省为占位符
repeat
if FLast^.Sym in [syGetValue] then
Error.SymUnexpected(Self);
if FLast^.Sym = syID then
if not IsPureId(FLast) then
Error.SymUnexpected(Self);
if FLast^.Sym <> syMod then
begin
last := simSymbol;
syntax.AddSynRec(last, FLast^.Sym, FLast^.Val); //==> 登记占位符
end
else
case PeekNextSym of
syLBlock: if last = simSymbol then //==> 要求必须被占位符间隔
begin
SymGotoNext;
SymTestNextPureID;
if (syntax.Name = FLast^.Val)
or not syntax.CanDeclare(FLast^.Val) then
Error.Redeclared(Self);
last := simParcel;
syntax.AddSynRec(last, syID, FLast^.Val); //==> 登记函数闭包
SymTestNext([syRBlock]);
end
else Error.SymUnexpected(Self);
syLParen: if last = simSymbol then // 要求必须被占位符间隔
begin
SymGotoNext;
SymTestNextPureID;
if (syntax.Name = FLast^.Val)
or not syntax.CanDeclare(FLast^.Val) then
Error.Redeclared(Self);
last := simValue;
syntax.AddSynRec(last, syID, FLast^.Val); //==> 登记表达式
SymTestNext([syRParen]);
end
else Error.SymUnexpected(Self);
else begin
last := simSymbol;
syntax.AddSynRec(last, FLast^.Sym, FLast^.Val);
end;
end;
SymGotoNext;
until (FLast^.Sym = syRParen) and (PeekNextSym = syLBlock);
SymGotoNext;
FCurFunc := syntax;
ParseBlock; //==> 语法函数主体
end;
从上面的代码可以看出,语法定义需要处理三种不同的对象:
1、占位符 :在Scheme/Lisp中称为关键字,主要用于分隔相临的参数(代码块)
2、表达式 :在语法块外即计算求值的代码块,通常用作流程控制条件。
3、函数闭包:执行具体语句的代码块,是实现语法功能的主体
发表评论
该博客是同时发布到论坛的,无法评论在论坛已被锁定的帖子
- 浏览: 3456 次
- 性别:

- 来自: 郑州

- 详细资料
搜索本博客
最近加入圈子
最新评论
-
lysee 官方网站开张营业
RednaxelaFX 2008-05-20引用啊,来迟了。刚刚有空 ...
-- by libudi -
lysee 官方网站开张营业
啊,来迟了。刚刚有空在JavaEye逛的时候才发觉Lysee有新进展了,加油! ...
-- by RednaxelaFX -
lysee 官方网站开张营业
我认为,托管资源目前来说,主要是内存。不是handle。所谓托管资源,也就是由g ...
-- by 梁利锋 -
lysee 官方网站开张营业
猜的不错的话 GC 回收的托管资源,比如,链表、字典、内存流之类的,通常都是非托 ...
-- by libudi -
lysee 官方网站开张营业
google 了一下 Notification 和 FreeNotificati ...
-- by 梁利锋






评论排行榜