浏览 5629 次
锁定老帖子 主题:Lysee 的语法定义
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-09-28
经过不懈努力,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 ! 语法定义扎根在函数闭包上,怎么优化函数闭包的生成、使用和释放,如何节省系统资源仍然还是个大问题。 总结一下:语法定义是个好东西,但使用的代价可能不菲,特别是过分滥用时会破坏我们已有的代码。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-09-28
Lysee的语法定义使用了一种非常懒(lazy)的算法,希望感兴趣的朋友能谈谈自己对语法定义的理解,不拘束于具体语言,无论是编译层面还是外在的语法层面,互相交流促进一下
|
|
返回顶楼 | |
发表时间:2007-09-28
下面这段代码是Lysee编译器遇到syntax关键字后的解析代码:
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、函数闭包:执行具体语句的代码块,是实现语法功能的主体 |
|
返回顶楼 | |
发表时间:2007-09-28
下面这段代码是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; 函数针对前面提到的三种对象进行分别的处理,目标都是调整好堆栈的状态。 在什么时候激发编译器应用新的语法定义要比单纯的解析语法声明过程和上面的堆栈调整过程要复杂的多,代码很难在整体上贴出,抱歉! |
|
返回顶楼 | |