Redy语法分析--一元运算符(+ - ~)

本文详细介绍了Redy语言中一元运算符'+', '-', '~'的解析过程及语法树构造方法,并提供了具体代码实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文地址: http://blog.chinaunix.net/uid-26750235-id-3145352.html


一元运算符(+ - ~ )

(unary operator(+ - ~))

()一元运算符

一元运算符是只有一个操作数的运算符,在Redy中有:'+' , '-' , '~' 这么三种,它们的运算优先级相等,结合方向为自右到左。当这三个运算符的操作数为整数,长整数时,它们代表着正号,负数,取反。例如:

代码1.1

  1. a=1
  2. -/*结果为-1*/
  3. +/*结果为1*/
  4. ----/*四个负号,结果为1*/
  5. ++++/*不管多少个正号,结果还是1*/
  6. +-+-+-/*正号对我没影响,可是负号有3个,结果为-1*/
  7. ~/*1取反,结果为-2*/
  8. ~~/*两次取反,还原了,结果为1*/

析解器解析代码1.1并为之构造语法树,需要分别用结点来表示这三个一元运算符。在Redy源码中,符号'+' 用结点AstNodePositive表示,符号'-'用结点AstNodeNegative表示,符号'~'用结点AstNodeNegated 表示,这三个结点都继承同一个结点AstNodeUExpr。用uml来表示它们之间的关系为:

1

1的内容为:

  1. AstNodeNegativeAstNodePositiveAstNodeNegated继承AstNodeUExpr

  2. AstNodeUExpr继承AstObject

  3. AstNodeUExpr由一个子结点s_value组成


()语法树实例1

下面来看一元运算符的实例。

代码1.2

  1. -+-1

在代码1.2中有3个一运算符和一个整数型的文字常量,要为之构造语法树,则需4个结点,三个一元运算符结点,一个文字常量结点(文字常量结点在前面文章中讲到)。具体的语法构如下:

2

要图2的语法树跑起来,需要从结点root来始执行,然后递归调用。AstNodeLiteral的执行方式前面已经讲到过,但还有两个节点AstNodePositiveAstNodeNegative的执行方式还没有实现。下面来看:

AstNodePositive的执行方式

代码1.3

  1. AstNodePositive.execute()
  2.     AstNodePositive.s_value.execute() /*执行子节点*/
  3.     value=reg0 /*子结点的执行后的结果,在寄存器reg0中*/
  4.     ret=value.positive() /*调用positive()方法*/
  5.     reg0=ret /*把结果保存存reg0中,供父结点使用*/
  6. end

其中需要说明的是;

1)在前面一章中,介绍了一个简单的虚拟机,该虚拟机有一个寄存器reg0。每个节点执行过后,都必须把结果放在寄存器reg0中。

2)在Redy中每一个对象,如果要支持某种运算符,那就必须实现该运算相对应的方法,例如要支持一元运算符'+',就必须实现positive方法;要支持一元运算符'-',就必须实现negative方法;要支持一元运算符'~',就必须实现negated方法。


AstNodeNegative的执行方式:

代码1.4

  1. AstNodeNegative.execute()
  2.     AstNodeNegative.s_value.execute() /*执行子节点*/
  3.     value=reg0 /*子结点的执行后的结果,在寄存器reg0中*/
  4.     ret=value.negative() /*调用negative()方法*/
  5.     reg0=ret /*把结果保存存reg0中,供父结点使用*/
  6. end

现在来运行图1中的语法树,一点问题都没有了。


()语法树实例2

代码1.5

  1. ~+-4

为代码1.5构造语法树为:

3

在实例1中,已经实现了AstNodeNegative,和AstNodePositive的执行方法,要让图3个语法树跑要来,还需要实现AstNodeNegated的执行方法,具体如下:

代码1.6

  1. AstNodeNegated.execute()
  2.     AstNodeNegated.s_value.execute() /*执行子节点*/
  3.     value=reg0 /*子结点的执行后的结果,在寄存器reg0中*/
  4.     ret=value.Negated() /*调用Negated()方法*/
  5.     reg0=ret /*把结果保存存reg0中,供父结点使用*/
  6. end

Ok,现在就可以执行图3的语法树。


返回文档首页 

附:  代码下载: git clone git://git.code.sf.net/p/redy/code redy-code

AstNodePositive ,AstNodeNegative, AstNodeNegated  位于src/syntax/  文件ast_node_unary_expr中


private void pictureBox1_Paint(object sender, PaintEventArgs e) { if (currentTransform != null) { e.Graphics.Transform = currentTransform; // 直接应用预计算矩阵 } e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; // 计算实际绘制区域 var clientRect = pictureBox1.ClientRectangle; // 1. 数据范围检查(添加容差) float epsilon = 1e-5f; float dataMinX = points.Min(p => p.fiveX); float dataMaxX = points.Max(p => p.fiveX); float dataMinY = points.Min(p => p.sexY); float dataMaxY = points.Max(p => p.sexY); if (Math.Abs(dataMaxX - dataMinX) < epsilon) { dataMinX -= 1f; dataMaxX += 1f; } if (Math.Abs(dataMaxY - dataMinY) < epsilon) { dataMinY -= 1f; dataMaxY += 1f; } // 2. 安全计算缩放比例 float scaleX = (dataMaxX - dataMinX) != 0 ? clientRect.Width / (dataMaxX - dataMinX) : 1f; float scaleY = (dataMaxY - dataMinY) != 0 ? clientRect.Height / (dataMaxY - dataMinY) : 1f; float zoomLevel = Math.Min(scaleX, scaleY) * 0.9f; // 3. 计算偏移量 float offsetX = -dataMinX * zoomLevel + (clientRect.Width - (dataMaxX - dataMinX) * zoomLevel) / 2; float offsetY = -dataMinY * zoomLevel + (clientRect.Height - (dataMaxY - dataMinY) * zoomLevel) / 2; // 应用变换前检查有效性 if (!float.IsInfinity(offsetX) && !float.IsInfinity(offsetY) && !float.IsNaN(offsetX) && !float.IsNaN(offsetY)) { e.Graphics.TranslateTransform(offsetX, offsetY); e.Graphics.ScaleTransform(zoomLevel, zoomLevel); currentTransform = e.Graphics.Transform; } else { e.Graphics.ResetTransform(); currentTransform = null; } // 4. 绘制点(添加坐标检查) foreach (var point in points) { float safeZoom = Math.Max(zoomLevel, 0.001f); float pointSize = Math.Min(4f / safeZoom, 100f); pointSize = Math.Max(pointSize, 0.1f); // 计算坐标并检查有效性 float blueX = point.fiveX - pointSize / 2; float blueY = point.sexY - pointSize / 2; if (IsValidCoordinate(blueX, blueY)) { e.Graphics.FillEllipse(Brushes.Blue, blueX, blueY, pointSize, pointSize); } float redX = point.eightX - pointSize / 2; float redY = point.nineY - pointSize / 2; if (IsValidCoordinate(redX, redY)) { e.Graphics.FillEllipse(Brushes.Red, redX, redY, pointSize, pointSize); } } if (currentTransform != null) { // 应用相同的变换矩阵 e.Graphics.Transform = currentTransform; // 在此绘制内容,坐标会自动适配 e.Graphics.DrawLine(Pens.Black, dataMinX, dataMinY, dataMaxX, dataMaxY); } }在加载的时候查看 public string hpw { get; set; } public string one { get; set; } public string two { get; set; } public string three { get; set; } public string fourname { get; set; } public float fiveX { get; set; } public float sexY { get; set; } public string sevenname { get; set; } public float eightX { get; set; } public float nineY { get; set; }one的数据这个数据代表颜色根据对应的颜色标注pictureBox1里点的颜色
最新发布
03-25
<think>嗯,用户给了一段C#代码,是关于pictureBox1_Paint方的,还提到了在加载时查看public string one { get; set; }这个属性,其中one的数据代表颜色,需要根据对应的颜色标注pictureBox1里点的颜色。用户的问题是要了解这段代码或者介绍它如何根据one的值来设置颜色。 首先,我需要理解用户提供的代码。pictureBox1_Paint方是用来处理PictureBox的绘制事件的。里面主要做了坐标变换,根据数据点的范围调整缩放和平移,使得数据点能正确显示在PictureBox的客户区域内。然后根据points集合中的每个点,绘制蓝色和色的椭圆。不过用户提到,one属性代表颜色,应该用这个属性来设置颜色,而不是固定的蓝色和色。 当前的代码中,绘制点的时候使用的是Brushes.Blue和Brushes.Red,是固定的颜色。用户希望根据每个点的one属性值来动态决定颜色。所以需要修改绘制椭圆的部分,根据point.one的值选择对应的画刷颜色。 首先,需要确认每个point对象是否有one属性。根据用户提供的属性列表,public string one { get; set; }是存在的,所以每个point应该有一个one属性存储颜色名称,比如"Red"、"Blue"等。 接下来,在绘制点的时候,需要将原本固定的Brushes.Blue和Brushes.Red替换为根据one的值动态获取的颜色。例如,使用Color.FromName(point.one)来获取对应的颜色,然后创建对应的SolidBrush。但需要注意,Brushes类中的预定义颜色是静态的,如果颜色名称是动态的,可能需要使用new SolidBrush(Color.FromName(...)),不过这样可能会引发资源管理的问题,需要确保brush被正确释放,或者使用缓存的画刷。 不过,在GDI+中,使用Brushes静态成员是安全的,因为它们由系统管理。但颜色名称可能不完全匹配,比如用户输入的可能是大小写不一致或者不存在的颜色名称,这时候需要处理默认情况,比如默认为黑色或者其他颜色。 另外,代码中每个点绘制了两次,分别用了fiveX/sexY和eightX/nineY,可能代表两个不同的坐标点,每个点都需要根据对应的颜色属性来绘制。但当前属性中只有one,可能需要确认这两个点的颜色是否都来自one,还是有其他属性。比如,可能每个点有一个主颜色,另一个颜色可能由另一个属性决定,但用户的问题里提到的是根据one的数据来标注颜色,所以可能只需要替换现有的蓝色和色为根据one的值。 但原代码中,每个点绘制了两个椭圆,一个是Blue,一个是Red,可能分别对应fiveX/sexY和eightX/nineY这两个坐标点,但用户的需求可能是将这两个点的颜色都根据one的值来设定,或者可能有其他属性。但根据用户的问题描述,可能每个点有一个颜色属性,所以需要确认。 例如,假设每个点有一个颜色,那么绘制两个坐标点时都使用该颜色,或者可能有不同的颜色属性。但根据用户提供的属性列表,每个点有one、two、three等字符串属性,可能每个属性对应不同的颜色。但用户明确说one的数据代表颜色,所以需要将颜色设置为one的值。 所以,修改代码中的Brushes.Blue和Brushes.Red为根据point.one的颜色值。例如: 将: e.Graphics.FillEllipse(Brushes.Blue, blueX, blueY, pointSize, pointSize); 和 e.Graphics.FillEllipse(Brushes.Red, redX, redY, pointSize, pointSize); 改为: var color = Color.FromName(point.one); var brush = new SolidBrush(color); e.Graphics.FillEllipse(brush, blueX, blueY, pointSize, pointSize); 同样对色部分也做同样的处理,不过这里可能需要确认是否每个点的eightX/nineY是否有不同的颜色属性,但根据用户的问题,可能one属性对应整个点的颜色,所以两个椭圆都应使用one的颜色。或者,可能蓝色对应one,色对应另一个属性,但用户没有说明。所以需要进一步分析。 不过用户的问题中明确说“根据对应的颜色标注pictureBox1里点的颜色”,而当前代码中每个点绘制了两个椭圆,可能这两个点需要根据不同的属性设置颜色。但用户只提到one属性,所以可能这两个点都应该使用one的颜色。或者,可能每个点有多个颜色属性,比如one和sevenname,但根据用户给出的属性列表,有sevenname,但用户未提及,所以需要假设用户希望根据one的值来设置这两个椭圆的颜色。 或者,用户可能希望根据one的值来设置其中一个椭圆,另一个保持固定或其他方式。但根据问题描述,可能整个点的颜色由one决定。因此,需要将蓝色和色的椭圆颜色都改为根据point.one的值来设定。 不过这可能不符合逻辑,因为原代码中绘制两个椭圆可能代表不同的数据,比如起点和终点,所以可能每个点需要不同的颜色。但用户的问题中只提到one属性代表颜色,因此可能需要进一步确认。但根据现有信息,只能假设需要根据one的值来设置颜色。 接下来需要考虑如何将字符串转换为颜色。例如,如果point.one是"Blue"、"Red"等已知颜色名称,可以使用Color.FromName,或者如果存储的是十六进制值,可能需要转换。但用户给出的one属性是字符串类型,可能直接存储颜色名称。 此外,需要考虑异常处理,比如当one的值无效时,使用默认颜色。例如: Color color; if (Enum.TryParse(point.one, out KnownColor knownColor)) { color = Color.FromKnownColor(knownColor); } else { color = Color.Black; } 或者使用Color.FromName,并处理未知颜色,如: var color = Color.FromName(point.one); if (color.A == 0 && color.R == 0 && color.G == 0 && color.B == 0 && point.one != "Black") { color = Color.Black; // 默认颜色 } 但这种方可能不准确,因为Color.FromName如果找不到名称,会返回Color空值(可能为透明?)。需要测试或查找文档。 另外,使用SolidBrush时需要注意资源释放,但如果在using语句中创建,可以自动释放。但在Paint事件中频繁创建和释放可能影响性能,可以考虑缓存常用的画刷,但需要权衡。 所以,修改后的代码可能如下: foreach (var point in points) { // 获取颜色 Color color; if (!string.IsNullOrEmpty(point.one) && Color.FromName(point.one).IsKnownColor) { color = Color.FromName(point.one); } else { color = Color.Black; // 默认颜色 } using (var brush = new SolidBrush(color)) { // 绘制第一个点(原蓝色) float blueX = point.fiveX - pointSize / 2; float blueY = point.sexY - pointSize / 2; if (IsValidCoordinate(blueX, blueY)) { e.Graphics.FillEllipse(brush, blueX, blueY, pointSize, pointSize); } // 绘制第二个点(原色) float redX = point.eightX - pointSize / 2; float redY = point.nineY - pointSize / 2; if (IsValidCoordinate(redX, redY)) { e.Graphics.FillEllipse(brush, redX, redY, pointSize, pointSize); } } } 但这样两个点会使用同一个颜色,即one属性的值。如果用户希望两个点有不同的颜色,可能需要不同的属性,比如one和sevenname,但用户的问题中只提到one,所以可能需要这样处理。 或者,用户可能希望每个点只绘制一个椭圆,但原代码中每个点绘制了两个椭圆,可能代表不同的数据。比如,fiveX/sexY和eightX/nineY是两个不同的坐标,可能需要不同的颜色。此时,可能需要两个颜色属性,比如one和sevenname,但用户只提到one,所以可能需要澄清。 不过,根据用户的问题描述,“one的数据代表颜色根据对应的颜色标注pictureBox1里点的颜色”,可能每个点有一个颜色属性,用于标注该点的颜色,所以不管绘制多少个坐标点,都使用同一个颜色。因此,修改后的代码应该将两个椭圆的颜色都设置为point.one对应的颜色。 另外,代码中可能存在的问题是,每个点绘制两个椭圆,但用户是否希望这两个椭圆使用不同的颜色?例如,原代码中的蓝色和色可能代表不同的含义,现在需要根据one的值统一颜色。或者,用户可能希望将原来的固定颜色替换为根据属性动态设置,比如蓝色椭圆用one的颜色,色椭圆用另一个属性(如sevenname)的颜色。但用户没有提到这点,所以需要根据问题描述判断。 根据用户的问题,重点是根据one的数据来设置颜色,所以可能只需要替换原有的颜色设置部分,将Brushes.Blue和Brushes.Red替换为从point.one获取的颜色。例如: 原来的蓝色部分: e.Graphics.FillEllipse(Brushes.Blue, blueX, blueY, pointSize, pointSize); 改为: var color = GetColorFromOne(point.one); using (var brush = new SolidBrush(color)) { e.Graphics.FillEllipse(brush, blueX, blueY, pointSize, pointSize); } 同样,色部分: e.Graphics.FillEllipse(Brushes.Red, redX, redY, pointSize, pointSize); 可能也需要修改,但不确定是否应该使用另一个属性。如果用户只提到one属性,可能这两个点都应使用one的颜色,或者可能只有其中一个点需要。但原问题描述不够明确。 但用户提供的属性列表中,每个点有fiveX、sexY、eightX、nineY,可能每个点需要绘制两个点,分别在不同的坐标,但颜色可能都来自one属性。或者,可能每个点的主颜色是one,另一个颜色是其他属性,比如sevenname,但用户未说明。 综上,假设用户希望根据one的值来设置这两个点的颜色,那么两个椭圆都应使用one的颜色。因此,修改后的代码应替换Brushes.Blue和Brushes.Red为从point.one获取的颜色。 另外,需要处理颜色转换时的错误情况,例如当one的值无效时,使用默认颜色。 可能的实现步骤: 1. 在绘制每个点时,获取point.one的颜色值。 2. 将颜色字符串转换为Color对象。 3. 创建对应的画刷。 4. 使用该画刷绘制椭圆。 同时,需要注意资源管理,使用using语句确保画刷被释放,或者在Paint事件中频繁创建和释放是否会影响性能。不过,在Paint事件中,这是常见的做,因为每次绘制都需要重新创建资源,而GDI+管理预定义的Brushes,但自定义颜色需要自己管理。 此外,原代码中的点大小计算可能不需要修改,但需要确保颜色正确应用。 可能的代码修改示例: 在foreach循环内部: foreach (var point in points) { // ... 原有的点大小计算 // 获取颜色 Color color; if (!string.IsNullOrEmpty(point.one)) { color = Color.FromName(point.one); // 检查是否为已知颜色 if (!color.IsKnownColor) { color = Color.Black; // 默认颜色 } } else { color = Color.Black; } using (SolidBrush brush = new SolidBrush(color)) { // 绘制第一个点(原蓝色) float blueX = point.fiveX - pointSize / 2; float blueY = point.sexY - pointSize / 2; if (IsValidCoordinate(blueX, blueY)) { e.Graphics.FillEllipse(brush, blueX, blueY, pointSize, pointSize); } // 绘制第二个点(原色) float redX = point.eightX - pointSize / 2; float redY = point.nineY - pointSize / 2; if (IsValidCoordinate(redX, redY)) { e.Graphics.FillEllipse(brush, redX, redY, pointSize, pointSize); } } } 这样,两个椭圆都会使用point.one指定的颜色。如果用户希望两个椭圆使用不同的颜色属性,可能需要使用不同的属性,例如one和sevenname,但根据问题描述,这里只需要处理one的情况。 另外,可能用户希望每个椭圆使用不同的颜色,比如fiveX/sexY使用one的颜色,而eightX/nineY使用另一个属性(如three)的颜色,这需要更多信息,但当前问题未提及。 总结,用户需要将pictureBox1中的点颜色根据one属性的值来设置,替换原有的固定颜色。因此,代码中的Brushes.Blue和Brushes.Red需要改为从point.one获取颜色,并处理无效颜色名称的情况,使用默认颜色。</think>以下是针对您代码的分析和改进建议,分步骤说明如何根据`one`属性的颜色值进行绘制: --- ### 一、原代码核心功能解析 1. **坐标变换系统**:通过计算缩放比例`zoomLevel`和偏移量`(offsetX, offsetY)`,将数据坐标映射到`PictureBox`的显示区域。 2. **数据安全机制**: - 数据范围检查(防止除零错误) - 有效性检查(过滤`NaN/Infinity`) - 动态调整点大小(`pointSize`随缩放比例变化) 3. **绘制逻辑**:每个数据点绘制两个固定颜色的圆形(蓝、),坐标分别来自`(fiveX, sexY)`和`(eightX, nineY)`。 --- ### 二、根据`one`属性设置颜色的改造步骤 #### 1. 颜色属性映射 ```csharp // 在绘制点之前添加颜色解析逻辑 Color color = Color.Black; // 默认颜色 if (!string.IsNullOrEmpty(point.one)) { // 尝试转换颜色名称(支持"Red", "Blue"等标准名称) color = Color.FromName(point.one); // 补充处理十六进制颜色值(如"#FF0000") if (color.A == 0 && point.one.StartsWith("#")) { try { color = ColorTranslator.FromHtml(point.one); } catch { /* 保持默认颜色 */ } } } ``` #### 2. 替换固定颜色画刷 **修改前**: ```csharp e.Graphics.FillEllipse(Brushes.Blue, blueX, blueY, pointSize, pointSize); e.Graphics.FillEllipse(Brushes.Red, redX, redY, pointSize, pointSize); ``` **修改后**: ```csharp using (SolidBrush dynamicBrush = new SolidBrush(color)) { // 绘制第一个点(原蓝色位置) e.Graphics.FillEllipse(dynamicBrush, blueX, blueY, pointSize, pointSize); // 绘制第二个点(原色位置) e.Graphics.FillEllipse(dynamicBrush, redX, redY, pointSize, pointSize); } ``` --- ### 三、完整修改后的绘制代码片段 ```csharp private void pictureBox1_Paint(object sender, PaintEventArgs e) { // ... [保留原有坐标变换逻辑] ... foreach (var point in points) { // ... [保留点大小计算逻辑] ... // 动态获取颜色 Color color = GetColorFromProperty(point.one); using (SolidBrush brush = new SolidBrush(color)) { // 绘制第一个点 if (IsValidCoordinate(blueX, blueY)) e.Graphics.FillEllipse(brush, blueX, blueY, pointSize, pointSize); // 绘制第二个点 if (IsValidCoordinate(redX, redY)) e.Graphics.FillEllipse(brush, redX, redY, pointSize, pointSize); } } // ... [保留其他绘制逻辑] ... } // 辅助方:从字符串解析颜色 private Color GetColorFromProperty(string colorName) { if (string.IsNullOrEmpty(colorName)) return Color.Black; Color color = Color.FromName(colorName); if (color.IsKnownColor) return color; // 尝试解析十六进制格式 try { return ColorTranslator.FromHtml(colorName); } catch { return Color.Black; } } ``` --- ### 四、关键注意事项 1. **颜色值格式**: - **命名颜色**:直接使用标准名称(如`"Red"`, `"DarkBlue"`) - **十六进制**:支持`#RRGGBB`或`#AARRGGBB`格式(如`"#FF00FF"`) 2. **性能优化**: - 使用`using`确保`SolidBrush`及时释放 - 可预缓存常用颜色画刷(需处理线程安全) 3. **错误处理**: - 无效颜色值回退到默认黑色 - 添加`try-catch`防止解析崩溃 --- ### 五、效果验证 1. 确保`one`属性赋值正确(如`point.one = "Green";`) 2. 观察绘制结果中两个圆形是否均变为目标颜色 3. 测试边界值(如空字符串、非颜色名) 通过上述改造,您的数据点颜色将完全由`one`属性动态控制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值